0%

生成器模式

生成器模式

背景

生成器模式使你能够分步创建复杂对象。假设有这样一个复杂对象, 在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。 这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中。

例如你想要示例化一个有车库、带游泳池和花园的房子对象。

一种实现方式是:设计一个”House”的基类,并让每种类型的房子继承自这个”House”基类,例如”HouseWithGarage”、”HouseWithGarden”、”HouseWithGarageAndGraden”等等。但是这种方式的结果就是你可能需要编写许多子类代码。

另外一种实现方式:无需生成子类,只设计一个”House”的类,同时这个”House”类拥有一个包括所有可能参数的超级构造函数,例如House(bool hasGarage, bool hasGraden, bool hasSwimmingPool, ...)。这种方式可以避免生成子类,但是如果需要增加一种房子类型,那么就不得不修改构造函数,这可能会导致使用之前构造函数的代码失效。另外这个超级构造函数中可能大多数参数最后都没有实际被使用到,导致构造函数的调用形式不简洁。

这种情况就可以考虑使用生成器模式了。

生成器模式简介

生成器模式将构造对象的代码从产品类中抽取出来,并将其放在一个名为生成器的独立类中。当创建对象的时候,是需要按需调用生成器提供的构造步骤即可。在一下情况下,你需要设计多种类型的生成器来构建出有相同接口但是表现形式不同的对象。

另外,你可以将用于创建对象的一系列的生成器调用步骤抽取出来形成一个单独的主管类。主管类非常适合放入各种例行构造流程,以便在程序中反复使用。

生成器代码样例

一个创建汽车和汽车使用手册的设计:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//other class a car builder that need
class CarEngine;
class GPS;


//a pure abstract class to use as an interface
class CarBuilder {
public:
virtual void reset() = 0;
virtual void setSeats(int number) = 0;
virtual void setEngine(const CarEngine &engine) = 0;
virtual void setGPS(const GPS &gps) = 0;
};

class Car;
//a car builder that build car itself
class CarCarBuilder: public CarBuilder {
private:
Car *_car;
public:
virtual void reset() override {
if (_car) {
delete _car;
}
_car = new Car();
}

virtual void setSeats(int number) override {
//set seat for car itself
}

virtual void setEngine(const CarEngine &engine) override {
//set engine for car itself
}

virtual void setGPS(const GPS &gps) override {
//set gps for car itself
}

virtual Car* getResult() {
return _car;
};
};

class CarManual;
//a car builder that build car's manual
class CarManualBuilder: public CarBuilder {
private:
CarManual *_manual;

public:
virtual void reset() override {
if (_manual) {
delete _manual;
}
_manual = new CarManual()
}

virtual void setSeats() override {
//add description for how to use seats in manual
}

virtual void setEngine(const CarEngine &engine) override {
//add car engine description in manual
}

virtual void setGPS(const GPS &pgs) override {
//add gps use guide in manual
}

virtual CarManual* getResult() override {
return _manual;
}
};

//a manager class that use builder to build car
class CarCarBuilderDirector {
public:
Car* buildSUV(CarBuilder &builder) {
builder.setSeats(4);
NormalEngine normalEngine;
builder.setEngine(normalEngine);
NormalGPS normalGps;
builder.setGPS(normalGps);
}

Car* buildSportsCar(CarBuilder &builder) {
builder.setSeats(2);
SportEngine sportEngine;
builder.setEngine(sportEngine);
SportCarGPS sportCarGps;
builder.setGPS(sportCarGps);
}
};

在上面的例子中,在CarCarBuilderDirector这个生成器主管类可以创建SUV类型的车辆对象,也可以创建SportsCar类型的对象。但在上例中我们没有定义基于CarBuilder接口的SUVCarBuilder类和SportsCarBuilder类。不是说不可以,而是说要结合具体的实际情况判断是否需要再定义SUVCarBuilder和SportsCarBuilder类,如果创建SUV和创建SportsCar的实现方式有差别的话,那么就可以再去定义SUVCarBuilder类和SportsCarBuilder类。

另外,我们在生成器接口中并没有提供获取构造结果对象的方法,因为不同生成器构造的产品可能没有公共接口, 因此你就不知道该方法返回的对象类型。 但是, 如果所有产品都位于单一类层次中, 你就可以安全地在基本接口中添加获取生成对象的方法。

总之还是那句话,不基于实际应用场景的设计模式都是耍流氓

总结

生成器模式让你可以分步骤生成对象, 而且允许你仅使用必须的步骤。 应用该模式后, 你再也不需要将几十个参数塞进构造函数里了。

基本生成器接口中定义了所有可能的制造步骤, 具体生成器将实现这些步骤来制造特定形式的产品。 同时, 主管类将负责管理制造步骤的顺序。