Builder
建造者模式(生成器模式)是一种创建型设计模式, 使你能够分步骤创建复杂对象。 该模式允许你使用相同的创建代码生成不同类型和形式的对象。
适用场景
-
折叠构造函数模式(telescoping constructor pattern )
假设你的构造函数中有十个可选参数, 那么调用该函数会非常不方便; 因此, 你需要重载这个构造函数, 新建几个只有较少参数的简化版。 但这些构造函数仍需调用主构造函数, 传递一些默认数值来替代省略掉的参数。
生成器模式让你可以分步骤生成对象, 而且允许你仅使用必须的步骤。 应用该模式后,你再也不需要将几十个参数塞进构造函数里了。
-
当你希望使用代码创建不同形式的产品 (例如石头或木头房屋) 时, 可使用生成器模式。
如果你需要创建的各种形式的产品, 它们的制造过程相似且仅有细节上的差异, 此时可使用生成器模式。
基本生成器接口中定义了所有可能的制造步骤, 具体生成器将实现这些步骤来制造特定形式的产品。 同时, 主管类将负责管理制造步骤的顺序。
-
使用生成器构造组合树或其他复杂对象。
生成器模式让你能分步骤构造产品。 你可以延迟执行某些步骤而不会影响最终产品。 你甚至可以递归调用这些步骤, 这在创建对象树时非常方便。
生成器在执行制造步骤时, 不能对外发布未完成的产品。 这可以避免客户端代码获取到不完整结果对象的情况。
生成器模式结构
以下是来自核心 Java 程序库的一些示例:
- java.lang.StringBuilder#append() (
非同步
) - java.lang.StringBuffer#append() (
同步
) - java.nio.ByteBuffer#put() (还有 CharBuffer、 ShortBuffer、IntBuffer、 LongBuffer、 FloatBuffer 和 DoubleBuffer)
- javax.swing.GroupLayout.Group#addComponent()
- java.lang.Appendable的所有实现
识别方法: 生成器模式可以通过类来识别, 它拥有一个构建方法和多个配置结果对象的方法。 生成器方法通常支持方法链 (例如 someBuilder->setValueA(1)->setValueB(2)->create()
)。
样例
关于如何复用相同的对象构造代码来生成不同类型的产品——例如汽车 (Car)——及其相应的使用手册 (Manual)。
传统的伪代码
// 只有当产品较为复杂且需要详细配置时,使用生成器模式才有意义。下面的两个
// 产品尽管没有同样的接口,但却相互关联。
class Car is
// 一辆汽车可能配备有 GPS 设备、行车电脑和几个座位。不同型号的汽车(
// 运动型轿车、SUV 和敞篷车)可能会安装或启用不同的功能。
class Manual is
// 用户使用手册应该根据汽车配置进行编制,并介绍汽车的所有功能。
// 生成器接口声明了创建产品对象不同部件的方法。
interface Builder is
method reset()
method setSeats(...)
method setEngine(...)
method setTripComputer(...)
method setGPS(...)
// 具体生成器类将遵循生成器接口并提供生成步骤的具体实现。你的程序中可能会
// 有多个以不同方式实现的生成器变体。
class CarBuilder implements Builder is
private field car:Car
// 一个新的生成器实例必须包含一个在后续组装过程中使用的空产品对象。
constructor CarBuilder() is
this.reset()
// reset(重置)方法可清除正在生成的对象。
method reset() is
this.car = new Car()
// 所有生成步骤都会与同一个产品实例进行交互。
method setSeats(...) is
// 设置汽车座位的数量。
method setEngine(...) is
// 安装指定的引擎。
method setTripComputer(...) is
// 安装行车电脑。
method setGPS(...) is
// 安装全球定位系统。
// 具体生成器需要自行提供获取结果的方法。这是因为不同类型的生成器可能
// 会创建不遵循相同接口的、完全不同的产品。所以也就无法在生成器接口中
// 声明这些方法(至少在静态类型的编程语言中是这样的)。
//
// 通常在生成器实例将结果返回给客户端后,它们应该做好生成另一个产品的
// 准备。因此生成器实例通常会在 `getProduct(获取产品)`方法主体末尾
// 调用重置方法。但是该行为并不是必需的,你也可让生成器等待客户端明确
// 调用重置方法后再去处理之前的结果。
method getProduct():Car is
product = this.car
this.reset()
return product
// 生成器与其他创建型模式的不同之处在于:它让你能创建不遵循相同接口的产品。
class CarManualBuilder implements Builder is
private field manual:Manual
constructor CarManualBuilder() is
this.reset()
method reset() is
this.manual = new Manual()
method setSeats(...) is
// 添加关于汽车座椅功能的文档。
method setEngine(...) is
// 添加关于引擎的介绍。
method setTripComputer(...) is
// 添加关于行车电脑的介绍。
method setGPS(...) is
// 添加关于 GPS 的介绍。
method getProduct():Manual is
// 返回使用手册并重置生成器。
// 主管只负责按照特定顺序执行生成步骤。其在根据特定步骤或配置来生成产品时
// 会很有帮助。由于客户端可以直接控制生成器,所以严格意义上来说,主管类并
// 不是必需的。
class Director is
private field builder:Builder
// 主管可同由客户端代码传递给自身的任何生成器实例进行交互。客户端可通
// 过这种方式改变最新组装完毕的产品的最终类型。
method setBuilder(builder:Builder)
this.builder = builder
// 主管可使用同样的生成步骤创建多个产品变体。
method constructSportsCar(builder: Builder) is
builder.reset()
builder.setSeats(2)
builder.setEngine(new SportEngine())
builder.setTripComputer(true)
builder.setGPS(true)
method constructSUV(builder: Builder) is
// ...
// 客户端代码会创建生成器对象并将其传递给主管,然后执行构造过程。最终结果
// 将需要从生成器对象中获取。
class Application is
method makeCar() is
director = new Director()
CarBuilder builder = new CarBuilder()
director.constructSportsCar(builder)
Car car = builder.getProduct()
CarManualBuilder builder = new CarManualBuilder()
director.constructSportsCar(builder)
// 最终产品通常需要从生成器对象中获取,因为主管不知晓具体生成器和
// 产品的存在,也不会对其产生依赖。
Manual manual = builder.getProduct()
链式调用
只更改 CarBuilder 部分
package creational.builder.builders;
import creational.builder.cars.Car;
import creational.builder.cars.CarType;
import creational.builder.components.Engine;
import creational.builder.components.GPSNavigator;
import creational.builder.components.Transmission;
import creational.builder.components.TripComputer;
public class CarBuilder implements IBuilder {
private CarType type;
private int seats;
private Engine engine;
private Transmission transmission;
private TripComputer tripComputer;
private GPSNavigator gpsNavigator;
public Car getResult() {
return new Car(type, seats, engine, transmission, tripComputer, gpsNavigator);
}
public CarBuilder(){}
public CarBuilder(Builder builder) {
this.type = builder.type;
this.seats = builder.seats;
this.engine = builder.engine;
this.transmission = builder.transmission;
this.tripComputer = builder.tripComputer;
this.gpsNavigator = builder.gpsNavigator;
}
public static class Builder {
private CarType type;
private int seats;
private Engine engine;
private Transmission transmission;
private TripComputer tripComputer;
private GPSNavigator gpsNavigator;
public Builder(CarType carType, int seats, Engine engine) {
this.type = carType;
this.seats = seats;
this.engine = engine;
}
public Builder setTransmission(Transmission transmission) {
this.transmission = transmission;
return this;
}
public Builder setTripComputer(TripComputer tripComputer) {
this.tripComputer = tripComputer;
return this;
}
public Builder setGpsNavigator(GPSNavigator gpsNavigator) {
this.gpsNavigator = gpsNavigator;
return this;
}
public CarBuilder build() {
return new CarBuilder(this);
}
}
//省略其余代码
}
调用
CarBuilder carBuilder = new CarBuilder.Builder(CarType.SPORTS_CAR, 2, new Engine(3.0, 0))
.setTransmission(Transmission.AUTOMATIC)
.build();
Car car = carBuilder.getResult();
System.out.println("Car built: \n" + car.getCarType());