0
点赞
收藏
分享

微信扫一扫

对建造者模式的理解

岛上码农 03-20 21:00 阅读 2

目录

一、为什么需要建造者模式?

1、场景1

  • 有时候,一个类中有很多字段,或者有些字段也是引用类型。这时候,当我们实例化这个类时(建造这个类的对象时),会比较麻烦:
  • 方案:提供入参巨多的构造器
public class ComplexClass {
	private String name;
	private String email;
	private Integer age;
	private Pet pet;
	...

	public ComplexClass(String name, String email, ...) {
	
	}
}

2、场景2

  • 有各种各样的房子,它们有一些共同的属性,也有一些独特的属性。
  • 一种设计是:抽象父类 + 多个子类。
  • 这可能导致子类爆炸,明明也是房子对象,却不得不搞一个新的房子类。

3、解决上述场景的办法:建造者模式

  • 示例:
public class House { 
	private List<Wall> walls;
	private List<Door> doors;
	...
}

public interface IHouseBuilder {
	void buildWalls(List<Wall> walls);
	void buildDoors(List<Door> doors);
	...
	House getProduct();
}

public class HouseWithGarageBuilder implements IHouseBuilder {
	private House house;
	public HouseWithGarageBuilder() {
		house = new House();
	}
	
	public buildWalls(List<Wall> walls) {
		this.walls = walls;
	}
	
	public buildDoors(List<Door> doors) {
		this.doors = doors;	
	}

	...
	
	public House getProduct() {
		return house;
	}
}

public class HouseDirector {
	private IHouseBuilder builder;

	public HouseDirector(IHouseBuilder builder) {
		this.builder = builder;
	}
	
	public House constructHouse() {
		builder.buildWalls(...);
		builder.buildDoors(...);
		return builder.getProduct();
	}
}
  • 本质:分步骤建造对象。

二、练习

1、题目描述 【来源】

2、输入描述

3、输出描述

4、输入示例

5、输出示例

6、参考

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Bike {
    private Frame frame;
    private Tires tires;

    @Override
    public String toString() {
        return frame.getType() + " " + tires.getType();
    }
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Frame {
    private String type;
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Tires {
    private String type;
}

public interface IBikeBuilder {
    void buildFrame();
    void buildTires();
    Bike getProduct();
}

public class MountainBikeBuilder implements IBikeBuilder {
    private Bike bike;

    public MountainBikeBuilder() {
        bike = new Bike();
    }

    @Override
    public void buildFrame() {
        bike.setFrame(new Frame("Aluminum Frame"));
    }

    @Override
    public void buildTires() {
        bike.setTires(new Tires("Knobby Tires"));
    }

    @Override
    public Bike getProduct() {
       return bike;
    }
}

public class RoadBikeBuilder implements IBikeBuilder {
    private Bike bike;

    public RoadBikeBuilder() {
        bike = new Bike();
    }

    @Override
    public void buildFrame() {
        bike.setFrame(new Frame("Carbon Frame"));
    }

    @Override
    public void buildTires() {
        bike.setTires(new Tires("Slim Tries"));
    }

    @Override
    public Bike getProduct() {
        return bike;
    }
}

public class BikeDirector {
    public Bike constructBike(IBikeBuilder bikeBuilder) {
        bikeBuilder.buildFrame();
        bikeBuilder.buildTires();
       return bikeBuilder.getProduct();
    }
}

public class Main {
    public static void main(String[] args) {
        BikeDirector bikeDirector = new BikeDirector();
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        scanner.nextLine();
        for (int i = 0; i < n; i++) {
            String type = scanner.nextLine();
            IBikeBuilder bikeBuilder;
            if ("mountain".equals(type)) {
                bikeBuilder = new MountainBikeBuilder();
            } else if ("road".equals(type)) {
                bikeBuilder= new RoadBikeBuilder();
            } else {
                throw new RuntimeException("type error");
            }
            Bike bike = bikeDirector.constructBike(bikeBuilder);
            System.out.println(bike.toString());
        }
    }
}

三、思考

  • 现实情况下,我还没这么用过建造者模式,因为按照上面的写法,还是很费劲的。

1、一般常用的是lombok库的@Builder注解。

  • 示例:
@Builder
@ToString
public class User {
    private String name;
    private Integer age;
    private String email;
    private String gender;
}

public class Main {
    public static void main(String[] args) {
        User user = User.builder()
                .name("Forrest")
                .age(20)
                .email("forrest@qq.com")
                .gender("male")
                .build();

        System.out.println(user);
    }
}
  • 编译后的User.class
public class User {
    private String name;
    private Integer age;
    private String email;
    private String gender;

    User(String name, Integer age, String email, String gender) {
        this.name = name;
        this.age = age;
        this.email = email;
        this.gender = gender;
    }

    public static UserBuilder builder() {
        return new UserBuilder();
    }

    public String toString() {
        return "User(name=" + this.name + ", age=" + this.age + ", email=" + this.email + ", gender=" + this.gender + ")";
    }

    public static class UserBuilder {
        private String name;
        private Integer age;
        private String email;
        private String gender;

        UserBuilder() {
        }

        public UserBuilder name(String name) {
            this.name = name;
            return this;
        }

        public UserBuilder age(Integer age) {
            this.age = age;
            return this;
        }

        public UserBuilder email(String email) {
            this.email = email;
            return this;
        }

        public UserBuilder gender(String gender) {
            this.gender = gender;
            return this;
        }

        public User build() {
            return new User(this.name, this.age, this.email, this.gender);
        }

        public String toString() {
            return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ", email=" + this.email + ", gender=" + this.gender + ")";
        }
    }
}
  • 可以清楚地看到,User中生成了一个内部类UserBuilder,来分步骤设置字段。

2、但@Builder注解有很多坑点

  • 显而易见的一点:
@Builder
@ToString
public class User {
    private String name = "Forrest";
    private Integer age;
    private String email;
    private String gender;
}

public class Main {
    public static void main(String[] args) {
        User user = User.builder()
                .age(20)
                .email("forrest@qq.com")
                .gender("male")
                .build();

        System.out.println(user);
    }
}

User(name=null, age=20, email=forrest@qq.com, gender=male)
  • 默认值就这样丢了…
@Builder
@ToString
@AllArgsConstructor
public class User {
    private String name = "Forrest";
    private Integer age;
    private String email;
    private String gender;

    public User(Integer age, String email, String gender) {
        this.age = age;
        this.email = email;
        this.gender = gender;
    }
}

public class Main {
    public static void main(String[] args) {
        User user = new User(20, "forrest@qq.com", "male");
        System.out.println(user);
    }
}

User(name=Forrest, age=20, email=forrest@qq.com, gender=male)
  • 不得不加上@AllArgsConstructor,因为@Builder生成的UserBuilder中用到了全参构造器。

3、不如用@Accessors(chain = true)

@Data
@Accessors(chain = true)
public class User {
    private String name = "Forrest";
    private Integer age;
    private String email;
    private String gender;
}

public class Main {
    public static void main(String[] args) {
        User user = new User()
                .setAge(18)
                .setEmail("forrest@qq.com")
                .setGender("male");
        System.out.println(user);
    }
}

User(name=Forrest, age=18, email=forrest@qq.com, gender=male)
举报

相关推荐

0 条评论