一、介绍
建造者模式、创建者模式、生成器模式,想叫什么就叫什么,它是将一个复杂的对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。创建者模式屏蔽了复杂对象的创建过程,它把复杂对象的创建过程加以抽象,通过子类继承或者重载的方式,动态的创建具有复合属性的对象。看懂概念,是高手,看不懂也没瓜西,看后面的案例。
二、角色说明
- 产品角色:一个具体的产品对象
- 抽象建造者:创建一个产品类对象的各个部件指定的接口或抽象类
- 具体建造者:实现接口,构建和装配各个部件
- 指挥者:构建一个使用建造者接口的对象,主要用于创建一个复杂的对象,在这个过程中,发挥的作用有两个: a、隔离了客户与对象的生产过程,b、负责控制产品对象的生产过程
三、案例——不用建造者模式,用传统方式实现造车需求
需求:假如需要制造一辆汽车,这个过程包括冲压、焊装、涂装、总装。无论制造什么汽车,都需要经历这四个工艺,如果不适用建造者模式,代码如下:
- 汽车制造工艺抽象类
/**
* 汽车制造工艺抽象类
*
* @author zhangxs
**/
public abstract class AbstractCar {
/** 冲压 */
public abstract void stamping();
/** 焊装 */
public abstract void welding();
/** 涂装 */
public abstract void painting();
/** 总装 */
public abstract void finalAssembly();
/** 交付 */
public void deliver() {
stamping();
welding();
painting();
finalAssembly();
}
}
- 我的宾利
/**
* 宾利
*
* @author zhangxs
**/
public class Bentley extends AbstractCar{
@Override
public void stamping() {
System.out.println("宾利冲压完成...");
}
@Override
public void welding() {
System.out.println("宾利焊装完成...");
}
@Override
public void painting() {
System.out.println("宾利涂装完成...");
}
@Override
public void finalAssembly() {
System.out.println("宾利总装完成...");
}
}
- 试了一下,很显然开起来并不爽
/**
* 测试
* 不用建造者模式的实现方式
* @author zhangxs
**/
public class Test {
public static void main(String[] args) {
Bentley bentley = new Bentley();
bentley.deliver();
}
}
- 优点:简单易于操作,非常好理解
- 缺点:程序结构过于简单,没有缓存层对象,扩展和维护不够友好,通俗的说,这样的方案,就好比把产品本身和产品的制造过程封装在了一起,杂乱无章,耦合性较高。
四、案例——用建造者模式创建复杂对象屏蔽创建过程
需求:还是制造汽车,嗯,这次造凯迪拉克和福特
- 发动机接口
/**
* 发动机接口
* 方便演示,定义函数式接口(有且仅有一个抽象方法 可以标注上@FunctionalInterface注解),便于调用,真正业务需求自行设计决定
* @author zhangxs
**/
public interface Engine {
void descEngine();
}
- 变速箱接口
/**
* 变速箱接口
* 方便演示,定义函数式接口(有且仅有一个抽象方法 可以标注上@FunctionalInterface注解),便于调用,真正业务需求自行设计决定
* @author zhangxs
**/
public interface Transmission {
void descTransmission();
}
- 底盘接口
/**
* 底盘接口
* 方便演示,定义函数式接口(有且仅有一个抽象方法 可以标注上@FunctionalInterface注解),便于调用,真正业务需求自行设计决定
* @author zhangxs
**/
public interface Underpan {
void descUnderpan();
}
- 产品角色,以三大件举例
/**
* 可定制三大件的汽车
* 产品角色:一个具体的产品
* @author zhangxs
**/
public class Car {
/** 发动机 */
private Engine engine;
/** 变速箱 */
private Transmission transmission;
/** 底盘 */
private Underpan underpan;
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public Transmission getTransmission() {
return transmission;
}
public void setTransmission(Transmission transmission) {
this.transmission = transmission;
}
public Underpan getUnderpan() {
return underpan;
}
public void setUnderpan(Underpan underpan) {
this.underpan = underpan;
}
}
- 抽象建造者
/**
* 抽象构建器
* 抽象建造者:创建一个产品类对象的各个部件指定的接口或抽象类
* @author zhangxs
**/
public abstract class CarBuilder {
protected Car car = new Car();
abstract void buildEngine();
abstract void buildTransmission();
abstract void buildUnderpan();
/**
* 返回产品
*/
public Car buildCar() {
return car;
}
}
- 具体建造者:凯迪拉克
/**
* 凯迪拉克定制构建类
* 具体的建造者:实现接口,构建和装配各个部件
* @author zhangxs
**/
public class CadillacBuilder extends CarBuilder {
@Override
void buildEngine() {
car.setEngine(() -> System.out.println("通用V8直喷发动机"));
}
@Override
void buildTransmission() {
car.setTransmission(() -> System.out.println("通用9AT变速箱"));
}
@Override
void buildUnderpan() {
car.setUnderpan(() -> System.out.println("基于通用EpsilonII平台,前麦弗逊独立悬架,后多连杆独立悬架"));
}
}
- 具体建造者:福特
/**
* 福特定制构建类
* 具体的建造者:实现接口,构建和装配各个部件
* @author zhangxs
**/
public class FordBuilder extends CarBuilder {
@Override
void buildEngine() {
car.setEngine(() -> System.out.println("EcoBoost发动机"));
}
@Override
void buildTransmission() {
car.setTransmission(() -> System.out.println("福特10AT变速箱"));
}
@Override
void buildUnderpan() {
car.setUnderpan(() -> System.out.println("前麦弗逊独立悬架,后多连杆独立悬架"));
}
}
- 指挥者:某种程度上可以理解为4儿子店
/**
* 指挥者类:以汽车制造这个需求来说,指挥者就是为了隔离客户与制造厂家的一个中间对象,某程度上可以理解为4S店,当然也许有更合适的,但意思就这么个意思
* 指挥者:构建一个使用建造者接口的对象,主要用于创建一个复杂的对象,在这个过程中,发挥的作用有两个:
* 1、隔离了客户与对象的生产过程
* 2、负责控制产品对象的生产过程
* @author zhangxs
**/
public class Director {
CarBuilder carBuilder = null;
/**
* 可以通过构造器传入CarBuilder
*/
public Director(CarBuilder carBuilder) {
this.carBuilder = carBuilder;
}
/**
* 可以通过Setter方式设置CarBuilder
*/
public void setCarBuilder(CarBuilder carBuilder) {
this.carBuilder = carBuilder;
}
/**
* 如何组装,交给指挥者
*/
public Car construct() {
carBuilder.buildEngine();
carBuilder.buildTransmission();
carBuilder.buildUnderpan();
return carBuilder.buildCar();
}
}
- 话不多说,弄
/**
* 测试
*
* @author zhangxs
**/
public class Test {
public static void main(String[] args) {
System.out.println("=====================凯迪拉克=====================");
showMyCar(new CadillacBuilder());
System.out.println("=======================福特======================");
showMyCar(new FordBuilder());
}
private static void showMyCar(CarBuilder carBuilder) {
Director director = new Director(carBuilder);
Car car = director.construct();
car.getEngine().descEngine();
car.getTransmission().descTransmission();
car.getUnderpan().descUnderpan();
}
}
五、案例——用建造者模式优化类构造器需传入较多参数的菜鸡代码
需求:嗯,又是汽车,这次用我朋友的奥迪RS7
1、不用建造者模式优化,除了显得不够大气,也没啥
- 汽车类
/**
* 汽车
*
* @author zhangxs
**/
public class Car {
/** 品牌 */
private String brand;
/** 级别 */
private String level;
/** 指导价 */
private Double price;
/** 厂商 */
private String vendor;
/** 能源类型 */
private String energyType;
public Car(String brand) {
this.brand = brand;
}
public Car(String brand, Double price) {
this.brand = brand;
this.price = price;
}
public Car(String brand, String level, Double price, String vendor, String energyType) {
this.brand = brand;
this.level = level;
this.price = price;
this.vendor = vendor;
this.energyType = energyType;
}
// ...可能还有更多的构造参数组合方式
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public String getVendor() {
return vendor;
}
public void setVendor(String vendor) {
this.vendor = vendor;
}
public String getEnergyType() {
return energyType;
}
public void setEnergyType(String energyType) {
this.energyType = energyType;
}
}
- 测试
/**
* 对象构建测试
*
* @author zhangxs
**/
public class Test {
public static void main(String[] args) {
// 构造器
Car audi = new Car("奥迪");
// Getter/Setter
audi.setEnergyType("48V轻混系统");
audi.setLevel("中大型车");
audi.setPrice(144.88);
audi.setVendor("Audi Sport");
System.out.printf("%sRs7:官方指导价%f万元,出自%s厂家,能源类型为%s,属于%s\n"
,audi.getBrand(),audi.getPrice(),audi.getVendor(),audi.getEnergyType(),audi.getLevel());
}
}
2、用了建造者模式优化之后,要多丝滑有多丝滑
- 发动机类
/**
* 发动机
*
* @author zhangxs
**/
public class Engine {
/** 型号 */
final String model;
/** 扭矩 */
final String torque;
/** 进气形式 */
final String airIntakeForm;
/** 气缸数 */
final Integer cylinders;
/** 排量 */
final String displacement;
private Engine(Builder builder) {
this.model = builder.model;
this.torque = builder.torque;
this.airIntakeForm = builder.airIntakeForm;
this.cylinders = builder.cylinders;
this.displacement = builder.displacement;
}
public String getModel() {
return model;
}
public String getTorque() {
return torque;
}
public String getAirIntakeForm() {
return airIntakeForm;
}
public Integer getCylinders() {
return cylinders;
}
public String getDisplacement() {
return displacement;
}
@Override
public String toString() {
return "Engine{" +
"model='" + model + '\'' +
", torque='" + torque + '\'' +
", airIntakeForm='" + airIntakeForm + '\'' +
", cylinders=" + cylinders +
", displacement='" + displacement + '\'' +
'}';
}
public static final class Builder {
private String model;
private String torque;
private String airIntakeForm;
private Integer cylinders;
private String displacement;
public Builder() {
}
public Builder setModel(String model) {
this.model = model;
return this;
}
public Builder setTorque(String torque) {
this.torque = torque;
return this;
}
public Builder setAirIntakeForm(String airIntakeForm) {
this.airIntakeForm = airIntakeForm;
return this;
}
public Builder setCylinders(Integer cylinders) {
this.cylinders = cylinders;
return this;
}
public Builder setDisplacement(String displacement) {
this.displacement = displacement;
return this;
}
/**
* 创建实体对象,掺入this,该this就是Builder对象
* @return builder.pattern.one.Engine
* @author zhangxs
* @date 2021-07-09 15:01
*/
public Engine build() {
return new Engine(this);
}
}
}
- 测试
/**
* 对象构建测试
*
* @author zhangxs
**/
public class Test {
public static void main(String[] args) {
// 构建者
Engine engine = new Engine
.Builder()
.setModel("DJP")
.setTorque("800N·m")
.setAirIntakeForm("双涡轮增压")
.setCylinders(8)
.setDisplacement("4.0T")
.build();
System.out.printf("奥迪Rs7:搭载发动机型号为%s,最大扭矩%s,进气形式为%s,包含%d个气缸,排量为%s\n"
,engine.getModel(),engine.getTorque(),engine.getAirIntakeForm(),engine.getCylinders(),engine.getDisplacement());
}
}
六、总结
- 适用场景
1、隔离复杂对象的创建和使用,相同的方法,不同执行顺序,产生不同事件结果
2、多个部件都可以装配到一个对象中,但产生的运行结果不相同
3、产品类非常复杂或者产品类因为调用顺序不同而产生不同作用
4、初始化一个对象时,参数过多,或者很多参数具有默认值
5、不适合创建差异性很大的产品类
6、产品内部变化复杂,会导致需要定义很多具体建造者类实现变化,增加项目中类的数量,增加系统的理解难度和运行成本
7、需要生成的产品对象有复杂的内部结构,这些产品对象具备共性; - 注意事项和细节
1、客户端即使用程序,不必知道产品内部的组成细节,将产品本身与产品的创建过程解耦,是的相同的创建过程可以创建不同的产品对象
2、每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便的替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象
3、可以更加精细的控制产品的创建过程,将复杂的产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程
4、增加新的具体建造者无需修改原有的相关代码,指挥者类针对抽象建造者类编程,系统扩展方便,符合开闭原则
5、建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式,因此使用的时候需要关注其适用范围
6、如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得庞大,因此在这种情况下,要慎重考虑,是否继续选择建造者模式 - 抽象工厂模式 vs 建造者模式
1、抽象工厂模式实现对产品家族的创建,一个产品家族是有这样特点的一系列产品:具有不同分类维度的产品组合,采用抽象工厂模式不需要关系构建过程,只关系什么产品由什么工厂生产即可
2、建造者模式则是需要按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新的产品