一、模式介绍
建造者模式是创建型模式的一种,其最大的特点就是允许调用者分步骤来创建复杂的对象。通常包含如下几种角色:
- 产品,指需要创建的产品对象;
- 建造者,根据不同的业务逻辑,将产品的不同属性进行建造;
- 主管类,包含多个建造者,根据业务场景的不同,来调用各个建造者完成建造;
- 调用者,通过主管类来获取建造好的产品对象;
下面来看下经典场景下的建造者模式:
/**
* 产品角色——OrdinaryCar
*/
@Data
public class OrdinaryCar {
private String name;
private String type;
private Double price;
private Integer EngineNum;
private String gps;
}
/**
* 建造者角色——OrdinaryCarBuilder
*/
public class OrdinaryCarBuilder {
private OrdinaryCar car = new OrdinaryCar();
public OrdinaryCarBuilder setName(String name) {
car.setName(name);
return this;
}
public OrdinaryCarBuilder setType(String type) {
car.setType(type);
return this;
}
public OrdinaryCarBuilder setPrice(Double price) {
car.setPrice(price);
return this;
}
public OrdinaryCarBuilder setEngineNum(Integer engineNum) {
car.setEngineNum(engineNum);
return this;
}
public OrdinaryCarBuilder setGps(String gps) {
car.setGps(gps);
return this;
}
public OrdinaryCar build() {
return car;
}
}
/**
* 产品角色——TankCar
*/
@Data
public class TankCar {
private String name;
private Integer weapon;
private Double weight;
private Double speed;
}
/**
* 建造者角色——TankCarBuilder
*/
public class TankCarBuilder {
private TankCar car = new TankCar();
public TankCarBuilder setCarName(String name){
car.setName(name);
return this;
}
public TankCarBuilder setCarWeapon(Integer weaponNum){
car.setWeapon(weaponNum);
return this;
}
public TankCarBuilder setCarWeight(Double weight){
car.setWeight(weight);
return this;
}
public TankCarBuilder setCarSpeed(Double speed){
car.setSpeed(speed);
return this;
}
public TankCar build() {
return car;
}
}
/**
* 主管角色——CarDirector
*/
public class CarDirector {
public void constructOrdinaryCar(OrdinaryCarBuilder builder) {
builder.setName("Honda")
.setType("SUV")
.setPrice(129900.00)
.setEngineNum(3)
.setGps("baidu map");
}
public void constructTankCar(TankCarBuilder builder) {
builder.setCarName("Tiger99")
.setCarWeapon(3)
.setCarWeight(12.22)
.setCarSpeed(70.00);
}
}
// 调用者角色
public static void main(String[] args) {
CarDirector director = new CarDirector();
OrdinaryCarBuilder builder1 = new OrdinaryCarBuilder();
director.constructOrdinaryCar(builder1);
log.info("当前ordinaryCar为:{}", builder1.build());
TankCarBuilder builder2 = new TankCarBuilder();
director.constructTankCar(builder2);
log.info("当前tankCar为:{}", builder2.build());
}
在如上的例子中,OrdinaryCar
和TankCar
是两种不太一样的产品,它们的建造过程和步骤都是不同的,因此我们需要两个建造类,来分别对应各自的建造过程。
建造类则是封装了产品的各方面属性的建造过程,同时支持了链式调用的写法,使得调用者在分步骤建造对象时更加简洁。
主管类角色的用途就是封装对各个产品对象的分步建造过程,使得调用者获得预制好的产品对象。比如我们买车的时候,低配、中配、高配的车型所包含内容都是4S店这个主管类提前预制好的,我们消费者作为调用者角色只能从主管类那里获取一个预制好的对象,而不能凭自己喜好分步骤自由地装配汽车零件及其功能。
在某些特殊的场景中,需要调用者自行分步骤地建造对象,那么主管类就不是必须的了,可以将主管类角色的职能挪到调用者角色上,比如:
public static void main(String[] args) {
OrdinaryCarBuilder builder = new OrdinaryCarBuilder()
.setName("Honda")
.setType("SUV")
.setPrice(169900.00)
.setEngineNum(3)
.setGps("baidu map");
log.info("当前ordinaryCar为:{}", builder.build());
TankCarBuilder builder2 = new TankCarBuilder()
.setCarName("Tiger99")
.setCarWeapon(3)
.setCarWeight(12.22)
.setCarSpeed(70.00);
log.info("当前tankCar为:{}", builder2.build());
}
在实际使用建造者模式的工程中,我们一般倾向于在产品类中增加静态建造类,来履行建造类的职能,如此可以减少单独的建造类和主管类,增加产品及其建造过程的内聚。
/**
* 产品角色——OrdinaryCar
*/
@Data
public class OrdinaryCar {
private String name;
private String type;
private Double price;
private Integer EngineNum;
private String gps;
public static class OrdinaryCarBuilder{
private OrdinaryCar car = new OrdinaryCar();
public OrdinaryCarBuilder setName(String name) {
car.setName(name);
return this;
}
public OrdinaryCarBuilder setType(String type) {
car.setType(type);
return this;
}
public OrdinaryCarBuilder setPrice(Double price) {
car.setPrice(price);
return this;
}
public OrdinaryCarBuilder setEngineNum(Integer engineNum) {
car.setEngineNum(engineNum);
return this;
}
public OrdinaryCarBuilder setGps(String gps) {
car.setGps(gps);
return this;
}
public OrdinaryCar build() {
return car;
}
}
}
/**
* 建造者角色——OrdinaryCarBuilder
*/
public class OrdinaryCarBuilder {
private OrdinaryCar car = new OrdinaryCar();
public OrdinaryCarBuilder setName(String name) {
car.setName(name);
return this;
}
public OrdinaryCarBuilder setType(String type) {
car.setType(type);
return this;
}
public OrdinaryCarBuilder setPrice(Double price) {
car.setPrice(price);
return this;
}
public OrdinaryCarBuilder setEngineNum(Integer engineNum) {
car.setEngineNum(engineNum);
return this;
}
public OrdinaryCarBuilder setGps(String gps) {
car.setGps(gps);
return this;
}
public OrdinaryCar build() {
return car;
}
}
public static void main(String[] args) {
OrdinaryCar car1 = new OrdinaryCar.OrdinaryCarBuilder()
.setName("Honda")
.setType("SUV")
.setPrice(129900.00)
.setEngineNum(3)
.setGps("baidu map")
.build();
log.info("当前ordinaryCar为:{}", car1);
TankCar car2 = new TankCar.TankCarBuilder()
.setCarName("Tiger99")
.setCarWeapon(3)
.setCarWeight(12.22)
.setCarSpeed(70.00)
.build();
log.info("当前tankCar为:{}", car2);
}
如此,使得建造类的使用更加方便简洁。
二、使用案例
2.1 JDK的StringBuffer及StringBuilder
public final class StringBuilder extends AbstractStringBuilder implements java.io.Serializable, CharSequence{
@Override
public StringBuilder append(String str) {
super.append(str);
return this;
}
}
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("hello")
.append(" ")
.append("world")
.append("!");
log.info("建造的字符串对象为:{}", sb.toString());
}
只不过这里StringBuffer
是重复调用同一个建造方法append
对唯一的产品(字符串)进行追加建造。
2.2 MyBatis中的SqlSessionFactoryBuilder
2.3 Spring中的BeanDefinitionBuilder
三、使用总结
3.1 优点
- 封装性好,对象的建造过程被建造者封装了;
- 扩展性好,不同产品的建造者是独立分离的,符合开闭原则;
- 便于对细节的修改,不同产品的各个属性修改,对其它产品或者同一产品的其它属性建造没有影响;
3.2 缺点
- 如果产品发生了变化,其对应的建造类也需要同步跟着变化,增加维护成本;
3.3 和工厂模式的区别
- 工厂模式创建对象无需分步骤,获取的产品对象完全一样;而建造者模式会因为建造的顺序不同,导致产出的产品不同(比如上面的StringBuilder);
- 建造者模式更适合构建复杂的对象,可以分步骤逐步充实产品特性,而工厂模式要求在创建对象的时候就需要把所有属性设置好;
网友评论