建造者模式

67 阅读5分钟

Builder Pattern.

别名:生成器模式


定义

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。


应用场景

1.避免 “ 重叠构造函数 ( telescopic constructor ) ”的出现

2.使用代码创建不同形式的产品

当你需要创建各种形式的产品,它们制造过程相似且仅有细节上的差异【基本生成器接口中定义了所有可能的制造步骤,具体生成器将实现这些步骤来制造特定形式的产品。同时,主管类将负责管理制造步骤的顺序】

3.使用生成器构造组合树或其他复杂对象

生成器模式让你能分步骤构造产品。则可以通过延迟执行某些步骤而不影响最终产品。你甚至可以通过递归这些步骤。【生成器不能对外发布未完成的产品。这样可以避免客户端代码获取不完整结果对象的情况】


实现方法

1.清晰地定义通用步骤,确保它们可以制造所有形式的产品。

2.在基本生成器接口声明这些步骤

3.为每个形式的产品创建具体生成器类,并实现其构造步骤

不要忘记实现获取构造结果对象的方法。不能在生成器接口中声明方法,因不同生成器构造的产品可能没有公共接口,因此不知道该方法返回的对象类型。但是,如果所有产品都位于单一类层次中,便可以安全地基本接口中获取生成对象的方法

4.考虑创建主管类。它可以使用同一生成器对象来封装多种构造产品的方法

5.客户端代码会同时创建生成器和主管对象。构造开始前,客户端必须将生成器对象传递给主管对象

6.只有在所有产品都遵循相同接口的情况下,构造结果可以直接通过主管类获取。否则,客户端应当通过生成器获取构造结构

Builder和Director是如何和一个客户协作的

sequenceDiagram
  participant Client
  participant Director
  participant ConcreteBuilder
  Client->>ConcreteBuilder:new ConcreteBuilder
  Client->> Director:new Director(ConcreteBuilder)
  Client->> Director:Construct()
  Director->>ConcreteBuilder:BuilderPartA()
  Director->>ConcreteBuilder:BuilderPartB()
  Director->>ConcreteBuilder:BuilderPartC()
  Client->>ConcreteBuilder:GetResult()

结构

UML图

classDiagram
Builder<--o Director
ConcreteBuilder1..|>Builder
ConcreteBuilder2..|>Builder
ConcreteBuilder1-->Product1
ConcreteBuilder2-->Product2
Client..>ConcreteBuilder1
Client-->Director
class Director{
	-builder:Builder
	+Direcotr(builder)
	+changeBuilder(builder)
	+make(type)
}
class Builder{
	<<interface>>
	+reset()
	+buildStepA()
	+buildStepB()
	+buildStepC()
}
class ConcreteBuilder1{
	-result:Product1
	+reset()
	+buildStepA()
	+buildStepB()
	+buildStepC()
	+getResult() Product1
}
class ConcreteBuilder2{
	-result:Product2
	+reset()
	+buildStepA()
	+buildStepB()
	+buildStepC()
	+getResult() Product2
}

参与者

1.生成器( Builder ) :接口声明在所有类型生成器中通用的产品构造步骤

2.具体生成器 ( Concrete Builder ) :提供构造过程的不同实现。具体生成器也可以构造不遵循通用接口的产品

3.产品( Products ) :是最终生成的对象。由不同生成器构造的产品无需属于同一类层次接口或接口

4.主管( Director ) :类定义调用构造步骤的顺序,这样你就可以创建和复用特定的产品配置

5.客户端( Client ) :必须与某个生成器对象与主管类关联


通用写法

public class Product1 {
   private String name;

   public void setName(String name) {
       this.name = name;
   }

   @Override
   public String toString() {
       return "Product1{" +
               "name='" + name + ''' +
               '}';
  }
}

public class Product2 {
   private String name;
​
   public void setName(String name) {
       this.name = name;
  }
​
   @Override
   public String toString() {
       return "Product2{" +
               "name='" + name + ''' +
               '}';
  }
}
​
public interface Builder {
   void reset();
​
   void buildStepA();
​
   void buildStepB();
​
   void buildStepC();
}
​
public class ConcreteBuilder1 implements Builder{
   private Product1 result;
​
   @Override
   public void reset() {
       result = new Product1();
  }
​
   @Override
   public void buildStepA() {
       result.setName("simple_1");
       System.out.println("Product1 --> StepA");
  }
​
   @Override
   public void buildStepB() {
       System.out.println("Product1 --> StepB");
  }
​
   @Override
   public void buildStepC() {
       System.out.println("Product1 --> StepC");
  }
​
   public Product1 getResult() {
       return result;
  }
}
​
public class ConcreteBuilder2 implements Builder{
   private Product2 result;
​
   @Override
   public void reset() {
       result = new Product2();
  }
​
   @Override
   public void buildStepA() {
       result.setName("simple_2");
       System.out.println("Product2 --> StepA");
  }
​
   @Override
   public void buildStepB() {
       System.out.println("Product1 --> StepB");
  }
​
   @Override
   public void buildStepC() {
       System.out.println("Product1 --> StepC");
  }
​
   public Product2 getResult() {
       return result;
  }
}
​
public class Director {
   private Builder builder;
​
   Director(Builder builder){
       this.builder = builder;
  }
​
   public void changeBuilder(Builder builder){
       this.builder = builder;
  }
​
   public void make(String type){
       builder.reset();
​
       if (type.equals("simple")){
           builder.buildStepA();
      }else {
           builder.buildStepB();
           builder.buildStepC();
      }
  }
}
​
public class Client {
   public static void main(String[] args) {
       ConcreteBuilder1 concreteBuilder1 = new ConcreteBuilder1();
       Director director = new Director(concreteBuilder1);
       director.make("simple");
       System.out.println(concreteBuilder1.getResult());
  }
}

案例

分步制造汽车

主管控制着构造顺序。 它知道制造各种汽车型号需要调用的生产步骤。 它仅与汽车的通用接口进行交互。 这样就能将不同类型的生成器传递给主管了。

最终结果将从生成器对象中获得, 因为主管不知道最终产品的类型。 只有生成器对象知道自己生成的产品是什么。

Components

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;
  }
}
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;
  }
}
public enum Transmission {
   SINGLE_SPEED, MANUAL, AUTOMATIC, SEMI_AUTOMATIC
}
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");
      }
  }
}

Builder

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);
}
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);
  }
}
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

public enum CarType {
   CITY_CAR, SPORTS_CAR, SUV
}
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;
  }
}
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;
  }
}

Director

public class Director {
​
   public void constructSportsCar(Builder builder) {
       builder.setCarType(CarType.SPORTS_CAR);
       builder.setSeats(2);
       builder.setEngine(new Engine(3.00));
       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.20));
       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.50));
       builder.setTransmission(Transmission.MANUAL);
       builder.setGPSNavigator(new GPSNavigator());
  }
}

Client

public class Client {
   public static void main(String[] args) {
       Director director = new Director();
​
       CarBuilder builder = new CarBuilder();
       director.constructSportsCar(builder);
​
       Car car = builder.getResult();
       System.out.println("Car built:\n" + car.getCarType());
​
​
       CarManualBuilder manualBuilder = new CarManualBuilder();
       
       director.constructSportsCar(manualBuilder);
       Manual carManual = manualBuilder.getResult();
       System.out.println("\nCar manual built:\n" + carManual.print());
  }
}