生成器模式

2022/11/16 设计模式

# 定义

# 生成器是一种创建型设计模式, 使你能够分步骤创建复杂对象。

与其他创建型模式不同, 生成器不要求产品拥有通用接口。 这使得用相同的创建过程生成不同的产品成为可能。

# 问题

假设有这样一个复杂对象, 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中; 甚至还有更糟糕的情况, 那就是这些代码散落在客户端代码的多个位置。

例如, 我们来思考如何创建一个 房屋House对象。 建造一栋简单的房屋, 首先你需要建造四面墙和地板, 安装房门和一套窗户, 然后再建造一个屋顶。 但是如果你想要一栋更宽敞更明亮的房屋, 还要有院子和其他设施 (例如暖气、 排水和供电设备), 那又该怎么办呢?

最简单的方法是扩展 房屋基类, 然后创建一系列涵盖所有参数组合的子类。 但最终你将面对相当数量的子类。 任何新增的参数 (例如门廊类型) 都会让这个层次结构更加复杂。

另一种方法则无需生成子类。 你可以在 房屋基类中创建一个包括所有可能参数的超级构造函数, 并用它来控制房屋对象。 这种方法确实可以避免生成子类, 但它却会造成另外一个问题。

通常情况下, 绝大部分的参数都没有使用, 这使得对于构造函数的调用十分不简洁。 例如, 只有很少的房子有游泳池, 因此与游泳池相关的参数十之八九是毫无用处的。

# 解决方案

生成器模式建议将对象构造代码从产品类中抽取出来, 并将其放在一个名为生成器的独立对象中。

该模式会将对象构造过程划分为一组步骤, 比如 build­Walls创建墙壁和 build­Door创建房门创建房门等。 每次创建对象时, 你都需要通过生成器对象执行一系列步骤。 重点在于你无需调用所有步骤, 而只需调用创建特定对象配置所需的那些步骤即可。

当你需要创建不同形式的产品时, 其中的一些构造步骤可能需要不同的实现。 例如, 木屋的房门可能需要使用木头制造, 而城堡的房门则必须使用石头制造。

在这种情况下, 你可以创建多个不同的生成器, 用不同方式实现一组相同的创建步骤。 然后你就可以在创建过程中使用这些生成器 (例如按顺序调用多个构造步骤) 来生成不同类型的对象。

例如, 假设第一个建造者使用木头和玻璃制造房屋, 第二个建造者使用石头和钢铁, 而第三个建造者使用黄金和钻石。 在调用同一组步骤后, 第一个建造者会给你一栋普通房屋, 第二个会给你一座小城堡, 而第三个则会给你一座宫殿。 但是, 只有在调用构造步骤的客户端代码可以通过通用接口与建造者进行交互时, 这样的调用才能返回需要的房屋。

# 主管

你可以进一步将用于创建产品的一系列生成器步骤调用抽取成为单独的主管类。 主管类可定义创建步骤的执行顺序, 而生成器则提供这些步骤的实现。

严格来说, 你的程序中并不一定需要主管类。 客户端代码可直接以特定顺序调用创建步骤。 不过, 主管类中非常适合放入各种例行构造流程, 以便在程序中反复使用。

此外, 对于客户端代码来说, 主管类完全隐藏了产品构造细节。 客户端只需要将一个生成器与主管类关联, 然后使用主管类来构造产品, 就能从生成器处获得构造结果了。

# 结构

# 伪代码

下面关于生成器模式的例子演示了你可以如何复用相同的对象构造代码来生成不同类型的产品——例如汽车 (Car)——及其相应的使用手册 (Manual)。

# 示例

# 分步制造汽车

# 1. 定义builders

  • 通用生成器接口: builder.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;

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

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  • 汽车生成器: builder.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;

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

    public void setSeats(int seats) {
        this.seats = seats;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void setTransmission(Transmission transmission) {
        this.transmission = transmission;
    }

    public void setTripComputer(TripComputer tripComputer) {
        this.tripComputer = tripComputer;
    }

    public void setGPSNavigator(GPSNavigator gpsNavigator) {
        this.gpsNavigator = gpsNavigator;
    }

    public Car getResult() {
        return new Car(type, seats, engine, transmission, tripComputer, gpsNavigator);
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
  • 汽车手册生成器: builder.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;

public class CarManualBuilder 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;
    }

    public void setSeats(int seats) {
        this.seats = seats;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public void setTransmission(Transmission transmission) {
        this.transmission = transmission;
    }

    public void setTripComputer(TripComputer tripComputer) {
        this.tripComputer = tripComputer;
    }

    public void setGPSNavigator(GPSNavigator gpsNavigator) {
        this.gpsNavigator = gpsNavigator;
    }

    public Manual getResult() {
        return new Manual(type, seats, engine, transmission, tripComputer, gpsNavigator);
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

# 2. 定义产品

  • 汽车产品: builder.cars.Car.java
package builder.cars;

import builder.components.Engine;
import builder.components.GPSNavigator;
import builder.components.Transmission;
import builder.components.TripComputer;

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 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 double getFuel() {
        return fuel;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  • 汽车手册产品: builder.cars.Manual.java
package builder.cars;

import builder.components.Engine;
import builder.components.GPSNavigator;
import builder.components.Transmission;
import builder.components.TripComputer;

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

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
  • 汽车类型: builder.cars.Car.java
package builder.cars;

public enum CarType {

    CITY_CAR,
    SPORT_CAR,
    SUV

}

1
2
3
4
5
6
7
8
9
10

# 3. 定义产品组件

  • 引擎: builder.components.Engine.java
package builder.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 this.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;
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  • 无线电: builder.components.Transmission.java
package builder.components;


public enum Transmission {

    SINGLE_SPEED,
    MANUAL,
    AUTOMIC,
    SEMI_AUTOMIC

}

1
2
3
4
5
6
7
8
9
10
11
12
  • GPS导航: builder.components.GPSNavigator.java
package builder.components;

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 this.route;
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  • 行程计算机: builder.components.TripComputer.java
package builder.components;

import builder.cars.Car;

public class TripComputer {

    private Car car;

    public void setCar(Car car) {
        this.car = car;
    }

    public void showFuelLevel(){
        System.out.println("Fuel level: ");
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 4. 主管控制生成器: builder.Director.java

package builder;

import builder.builders.Builder;
import builder.cars.CarType;
import builder.components.Engine;
import builder.components.GPSNavigator;
import builder.components.Transmission;
import builder.components.TripComputer;

public class Director {

    public void constructSportsCar(Builder builder) {
        builder.setCarType(CarType.SPORT_CAR);
        builder.setSeats(2);
        builder.setEngine(new Engine(3.0, 0));
        builder.setTransmission(Transmission.SEMI_AUTOMIC);
        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.AUTOMIC);
        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());
    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

# 5. 客户端代码

package builder;

import builder.builders.CarBuilder;
import builder.builders.CarManualBuilder;
import builder.cars.Car;
import builder.cars.Manual;

public class Demo {

    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 carManualBuilder = new CarManualBuilder();

        director.constructSportsCar(carManualBuilder);

        Manual manual = carManualBuilder.getResult();

        System.out.println("Car manual built: \n" + manual.print());

    }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

# 6. 输出

Last Updated: 2022/11/15 23:19:57