1. 意图
将一个复杂对象的创建与它的表示分离,使得同样的创建过程可以创建不同的表示
2. 动机
- 软件系统中,有时候面临着一个复杂对象的创建工作,通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将他们组合在一起的算法相对稳定
- 如何应对这种变化?如何提供一种封装机制来隔离出复杂对象的各个部分的变化,从而保持系统中的稳定构建算法不随需求改变而改变
3. 适用性
- 使用生成器可避免重叠构造函数(telescopic constructor)的出现
假设构造函数中有十个可选参数,那么调用该函数会非常不方便;因此,需要重载这个构造函数,新建几个只有较少参数的简化版。但是这些构造函数仍需调用主构造函数,传递一些默认数值来代替省略掉的参数
class Pizza { Pizza(int size) { ... } Pizza(int size, boolean cheese) { ... } Pizza(int size, boolean cheese, boolean pepperoni) { ... } // ...
- 当创建复杂对象的算法应该独立于该对象的组成部分以及他们的装配方式时
- 当构造过程必须允许被构造对象有不同的表示时
4. 结构
5. 效果
1) 可以改变一个产品的内部表示 Builder对象提供给导向器一个构造产品的抽象接口,产品是通过抽象接口构造的。该接口使得生成器可以隐藏这个产品的表示和内部结构。当时需要改变产品的内部结构,只需要定义一个新的生成器
2) 将构造代码和表示代码分开 Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。每个Concretebuilder包含了创建和装配一个特定产品的所有代码。Director可以复用它以在相同部件集合的基础上构建不同的product
3)对构造过程进行更精细的控制 Builder模式是在导向器的控制下,一步一步构造产品的。仅当产品完成时,导向器才从生成器中取回它。因此,builder接口相比其他创建型模式能更好的反映产品的构造过程,这使我们可以更精细的控制构造过程
6. 代码实现
Builers
builders/Builder.java: 通用生成器接口
package builder.builders; import builder.cars.CarType; import builder.components.Engine; import builder.components.GPSNavigator; import builder.components.Transmission; import builder.components.TripComputer; /** * @author GaoMing * @date 2021/7/18 - 9:58 * Builder interface defines all possible ways to configure a product */ public interface Builder { void setCarType(CarType type); void setSeats(int seats); void setEngine(Engine engine); void setTransmission(Transmission transmission); void setTripComputer(TripComputer tripComputer); void setGPSNavigator(GPSNavigator gpsNavigator); }
builders/CarBuilder.java: 汽车生成器
package builder.builders; import builder.cars.Car; import builder.cars.CarType; import builder.components.Engine; import builder.components.GPSNavigator; import builder.components.Transmission; import builder.components.TripComputer; /** * @author GaoMing * @date 2021/7/18 - 10:01 */ public class CarBuilder implements Builder{ private CarType type; private int seats; private Engine engine; private Transmission transmission; private TripComputer tripComputer; private GPSNavigator gpsNavigator; public void setCarType(CarType type) { this.type = type; } @Override public void setSeats(int seats) { this.seats = seats; } @Override public void setEngine(Engine engine) { this.engine = engine; } @Override public void setTransmission(Transmission transmission) { this.transmission = transmission; } @Override public void setTripComputer(TripComputer tripComputer) { this.tripComputer = tripComputer; } @Override public void setGPSNavigator(GPSNavigator gpsNavigator) { this.gpsNavigator = gpsNavigator; } public Car getResult() { return new Car(type, seats, engine, transmission, tripComputer, gpsNavigator); } }
builders/CarManualBuilder.java: 汽车手册生成器
package builder.builders; import builder.cars.CarType; import builder.cars.Manual; import builder.components.Engine; import builder.components.GPSNavigator; import builder.components.Transmission; import builder.components.TripComputer; /** * @author GaoMing * @date 2021/7/18 - 10:03 * Unlike other creational patterns, Builder can construct unrelated products, * which don't have the common interface. * In this case we build a user manual for a car, using the same steps as we * built a car. This allows to produce manuals for specific car models, * configured with different features. */ public class CarManualBuilder implements Builder{ private CarType type; private int seats; private Engine engine; private Transmission transmission; private TripComputer tripComputer; private GPSNavigator gpsNavigator; @Override public void setCarType(CarType type) { this.type = type; } @Override public void setSeats(int seats) { this.seats = seats; } @Override public void setEngine(Engine engine) { this.engine = engine; } @Override public void setTransmission(Transmission transmission) { this.transmission = transmission; } @Override public void setTripComputer(TripComputer tripComputer) { this.tripComputer = tripComputer; } @Override public void setGPSNavigator(GPSNavigator gpsNavigator) { this.gpsNavigator = gpsNavigator; } public Manual getResult() { return new Manual(type, seats, engine, transmission, tripComputer, gpsNavigator); } }
Cars
cars/Car.java: 汽车产品
package builder.cars; import builder.components.Engine; import builder.components.GPSNavigator; import builder.components.Transmission; import builder.components.TripComputer; /** * @author GaoMing * @date 2021/7/18 - 9:48 * Car is a product class */ public class Car { private final CarType carType; private final int seats; private final Engine engine; private final Transmission transmission; private final TripComputer tripComputer; private final GPSNavigator gpsNavigator; private double fuel = 0; public Car(CarType carType, int seats, Engine engine, Transmission transmission, TripComputer tripComputer, GPSNavigator gpsNavigator) { this.carType = carType; this.seats = seats; this.engine = engine; this.transmission = transmission; this.tripComputer = tripComputer; if (this.tripComputer != null) { this.tripComputer.setCar(this); } this.gpsNavigator = gpsNavigator; } public CarType getCarType() { return carType; } public double getFuel() { return fuel; } public void setFuel(double fuel) { this.fuel = fuel; } public int getSeats() { return seats; } public Engine getEngine() { return engine; } public Transmission getTransmission() { return transmission; } public TripComputer getTripComputer() { return tripComputer; } public GPSNavigator getGpsNavigator() { return gpsNavigator; } }
cars/Manual.java: 手册产品
package builder.cars; import builder.components.Engine; import builder.components.GPSNavigator; import builder.components.Transmission; import builder.components.TripComputer; /** * @author GaoMing * @date 2021/7/18 - 9:56 * Car manual is another product. Note that it does not have the same ancestor as a Car. They are not related */ public class Manual { private final CarType carType; private final int seats; private final Engine engine; private final Transmission transmission; private final TripComputer tripComputer; private final GPSNavigator gpsNavigator; public Manual(CarType carType, int seats, Engine engine, Transmission transmission, TripComputer tripComputer, GPSNavigator gpsNavigator) { this.carType = carType; this.seats = seats; this.engine = engine; this.transmission = transmission; this.tripComputer = tripComputer; this.gpsNavigator = gpsNavigator; } public String print() { String info = ""; info += "Type of car: " + carType + "\n"; info += "Count of seats: " + seats + "\n"; info += "Engine: volume - " + engine.getVolume() + "; mileage - " + engine.getMileage() + "\n"; info += "Transmission: " + transmission + "\n"; if (this.tripComputer != null) { info += "Trip Computer: Functional" + "\n"; } else { info += "Trip Computer: N/A" + "\n"; } if (this.gpsNavigator != null) { info += "GPS Navigator: Functional" + "\n"; } else { info += "GPS Navigator: N/A" + "\n"; } return info; } }
cars/CarType.java
package builder.cars; /** * @author GaoMing * @date 2021/7/18 - 9:51 */ public enum CarType { CITY_CAR, SPORTS_CAR, SUV }
Components
components/Engine.java: 产品特征 1
package builder.components; /** * @author GaoMing * @date 2021/7/18 - 9:27 * feature of a car */ public class Engine { private final double volume; private double mileage; private boolean started; public Engine(double volume, double mileage){ this.volume = volume; this.mileage = mileage; } public void on(){ started = true; } public void off(){ started = false; } public boolean isStarted(){ return started; } public void go(double mileage){ if(started){ this.mileage += mileage; }else { System.err.println("Cannot go(), you must start engine first!"); } } public double getVolume(){ return volume; } public double getMileage(){ return mileage; } }
components/GPSNavigator.java: 产品特征 2
package builder.components; /** * @author GaoMing * @date 2021/7/18 - 9:33 * Another feature of a car */ public class GPSNavigator { private String route; public GPSNavigator(){ this.route = "221b, Baker Street, London to Scotland Yard, 8-10 Broadway, London"; } public GPSNavigator(String manualRoute){ this.route = manualRoute; } public String getRoute(){ return route; } }
components/Transmission.java: 产品特征 3
package builder.components; /** * @author GaoMing * @date 2021/7/18 - 9:35 * Another feature of a car */ public enum Transmission { SINGLE_SPEED, MANUAL, AUTOMATIC, SEMI_AUTOMATIC }
components/TripComputer.java: 产品特征 4
package builder.components; import builder.cars.Car; /** * @author GaoMing * @date 2021/7/18 - 9:48 * Another feature of a car */ public class TripComputer { private Car car; public void setCar(Car car){ this.car = car; } public void showFuellevel(){ System.out.println("Fuel level: " + car.getFuel()); } public void showStatus(){ if(this.car.getEngine().isStarted()){ System.out.println("Car is started"); }else { System.out.println("Car isn't started"); } } }
Director
director/Director.java: 主管控制生成器
package builder.director; import builder.cars.CarType; import builder.components.Engine; import builder.components.GPSNavigator; import builder.components.Transmission; import builder.components.TripComputer; import builder.builders.Builder; /** * @author GaoMing * @date 2021/7/18 - 10:08 * Director defines the order of building steps. It works with a builder object * through common Builder interface. Therefore it may not know what product is * being built. */ public class Director { public void constructSportsCar(Builder builder) { builder.setCarType(CarType.SPORTS_CAR); builder.setSeats(2); builder.setEngine(new Engine(3.0, 0)); builder.setTransmission(Transmission.SEMI_AUTOMATIC); builder.setTripComputer(new TripComputer()); builder.setGPSNavigator(new GPSNavigator()); } public void constructCityCar(Builder builder) { builder.setCarType(CarType.CITY_CAR); builder.setSeats(2); builder.setEngine(new Engine(1.2, 0)); builder.setTransmission(Transmission.AUTOMATIC); builder.setTripComputer(new TripComputer()); builder.setGPSNavigator(new GPSNavigator()); } public void constructSUV(Builder builder) { builder.setCarType(CarType.SUV); builder.setSeats(4); builder.setEngine(new Engine(2.5, 0)); builder.setTransmission(Transmission.MANUAL); builder.setGPSNavigator(new GPSNavigator()); } }
Demo.java: 客户端代码
package builder; import builder.builders.CarBuilder; import builder.builders.CarManualBuilder; import builder.cars.Car; import builder.cars.Manual; import builder.director.Director; /** * @author GaoMing * @date 2021/7/18 - 10:13 * Demo class. Everything comes together here. */ public class demo { public static void main(String[] args) { Director director = new Director(); // Director gets the concrete builder object from the client // (application code). That's because application knows better which // builder to use to get a specific product. CarBuilder builder = new CarBuilder(); director.constructSportsCar(builder); // The final product is often retrieved from a builder object, since // Director is not aware and not dependent on concrete builders and // products. Car car = builder.getResult(); System.out.println("Car built:\n" + car.getCarType()); CarManualBuilder manualBuilder = new CarManualBuilder(); // Director may know several building recipes. director.constructSportsCar(manualBuilder); Manual carManual = manualBuilder.getResult(); System.out.println("\nCar manual built:\n" + carManual.print()); } }
OutputDemo.txt: 执行结果
Car built: SPORTS_CAR Car manual built: Type of car: SPORTS_CAR Count of seats: 2 Engine: volume - 3.0; mileage - 0.0 Transmission: SEMI_AUTOMATIC Trip Computer: Functional GPS Navigator: Functional
7. 与其他模式的关系
-
生成器重点关注如何分步生成复杂对象。 抽象工厂专门用于生产一系列相关对象。 抽象工厂会马上返回产品, 生成器则允许你在获取产品前执行一些额外构造步骤。
-
Composite通常是由Builder生成的
- 与其他创建型模式不同,生成器不要求产品拥有通用的接口,这使得相同的创建过程生成不同的产品成为可能。
8. 已知应用
- 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() )