建造者模式 - STEMHA's Blog

建造者模式

建造者模式是什么 ?

顾名思义,建造者模式用于构建对象。

  • 有时,我们创建的对象可能很复杂,可能由多个子对象组成,或者需要复杂的构建过程。通过使用建造者模式,可以简化创建复杂类型的过程。
  • 建造者模式通常用来构建复合或者聚合的对象
  • 严谨地说,构建器模式封装或隐藏了构建复杂对象的过程,并将对象的表示和构建进行了分离。这种分离使我们可以使用相同的构建过程来构建不同的表示形式
  • 建造者模式是一个非常实用而常见的创建类型的模式(creational design pattern)

应用场景:

  • 当一个类的构造函数参数个数超过4个,而且这些参数有些是可选的参数,考虑使用构造者模式。

建造者模式类图

类图由以下实体组成

  • Builder:其定义了构建Product的抽象步骤,其实体类需要实现这些步骤。其会包含一个用来返回最终产品的方法Product getProduct()。
  • Concrete Builder:具体的建造者
  • Director :决定如何构建最终产品的算法. 其会包含一个负责组装的方法void Construct(Builder builder), 在这个方法中通过调用builder的方法,就可以设置builder,等设置完成后,就可以通过builder的 getProduct() 方法获得最终的产品。
  • Product:最终要生成的对象

使用建造者模式造不同的飞机

接下来的示例讨论了如何将表示和构造复杂的对象两个操作耦合在一起。

示例:
以制造飞机为例,假设飞机的制造过程分三个步骤进行:

  • 驾驶舱的制作
  • 引擎
  • 机翼

在我们的假设中,每架飞机至少需要上述三个步骤。但是,如果飞机是客机的话,我们就需要增加在飞机上建造浴室的步骤。这些步骤代表了构建过程。我们建造的产品可以是不同的形式。文中的例子是飞机,但可以有不同的表示形式的飞机,例如战斗机或客机。但是使用相同的建造过程,我们应该能够同时生产战斗机和客机。

现在看一些代码。首先,我们将从AircraftBuilder类的抽象接口开始。

  • The builder contains a method for each component that can be part of the final product. These methods are selectively overridden by concrete builders depending on if the builders will be including that part in the final product variant that they are responsible for building.
  • 抽象类Builder会包含最终产品每个部分的构建方法。
  • 这些方法会被具体的构建者(concrete builders)有选择地覆盖,这取决于具体的构建者负责的最终产品中是否包括该部分,如果包含,就覆盖对应的方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AircraftBuilder {
public:
virtual void buildEngine() { //引擎

}

virtual void buildWings() { //机翼

}

virtual void buildCockpit() { //驾驶舱
}

virtual void buildBathrooms() { //洗手间

}

virtual IAircraft& getResult() = 0; //返回最终产品的方法
}

现在,我们将实施两个具体的建造者,一个用于F-16,另一个用于波音747。

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
class Boeing747Builderpublic AircraftBuilder {
Boeing747 boeing747;
public:
virtual void buildCockpit() override //选择覆盖驾驶舱建造方法
{

}

virtual void buildEngine() override //选择覆盖引擎建造方法
{

}

virtual void buildBathrooms() override //选择覆盖洗手间建造方法
{

}

virtual void buildWings() override //选择覆盖机翼建造方法
{

}

virtual IAircraft& getResult() override //返回最终产品的方法
{
return boeing747;
}
}

class F16Builder extends AircraftBuilder {
F16 f16;
public:
virtual void buildEngine() override //选择覆盖引擎建造方法
{
// get F-16 an engine
// f16.engine = new F16Engine();
}

virtual void buildWings() override //选择覆盖机翼建造方法
{
// get F-16 wings
// f16.wings = new F16Wings();
}

virtual void buildCockpit() override //选择覆盖驾驶舱建造方法
{
f16 = new F16();
// get F-16 a cockpit
// f16.cockpit = new F16Cockpit();
}

virtual IAircraft& getResult() override //返回最终产品的方法
{
return f16;
}
}

为简便起见,我们提供了建造器的框架,并跳过了每种方法的单独实现。

请注意,F16Builder它不会覆盖该buildBathrooms方法,因为F-16座舱中没有浴室。由于波音747拥有供乘客使用的浴室,因此波音的制造商确实重写了浴室的方法。

建造飞机所需的过程或算法(在我们的案例中是创建不同零件的特定顺序)被称为的另一个类Director捕获。

Director类在某种意义上指导着飞机的建造。最终产品仍由建造者(Builders)返回(return)。这里的意思是说Director类知识用来配置建造过程中的参数,最中的产品仍然在建造者那里,所以需要建造者(Builders)返回(return)产品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Director {
AircraftBuilder aircraftBuilder; //捕获Builder,也就是Builder是Director的一份子
public:

Director(AircraftBuilder aircraftBuilder) //构造函数
{
this->aircraftBuilder = aircraftBuilder;
}

void construct(bool isPassenger) //构建过程(可以有各种参数,来配置构建过程)
{
aircraftBuilder.buildCockpit();
aircraftBuilder.buildEngine();
aircraftBuilder.buildWings();

if (isPassenger)
aircraftBuilder.buildBathrooms();
}
}

请注意,我们如何通过我们选择的制造商,并将飞机产品(表示形式)更改为F-16或Boeing-747。

在我们的方案中,建造者返回相同的超类,但是如果构建器返回的产品不太相似,则情况可能并非如此。

客户端将使用以下模式:

1
2
3
4
5
6
7
8
9
10
class Client {
public:
void Clientmain() {
F16Builder f16Builder = F16Builder(); //先初始化一个建造者
Director director = Director(f16Builder); //然后交给指导者来配参数
director.construct(false); //指导建造过程

IAircraft f16 = f16Builder.getResult(); //建造者接受指导后返回最终的产品
}
}
  • The AircraftBuilder interface hides how a given aircraft gets built.
  • The client is unaware of the classes F16Engine, F16Cockpit and similar classes for Boeing-747.

假如没有指导者

如果在没有Director的情况下使用了构建器模式。客户端可以直接实例化Builder并调用所需的方法以获取自身的产品。
而且也是一个应对“(伸缩构建)telescoping constructors”的解法:

  • 假设一个类具有很多的属性,但是有些属性是可选的。在这种情况下,可以调用Builder仅设置必需的属性并创建产品。

其他例子

另一个假设的示例可能是创建pdf或html类型的文档。请考虑以下代码段:

1
2
3
4
5
6
7
8
9
10
11
12
13
IDocument construct(DocumentBuilder documentBuilder) 
{
documentBuilder.addTitle("Why use design patterns");
documentBuilder.addBody("blah blah blah... more blah blah blah");
documentBuilder.addAuthor("C. H. Afzal");
documentBuilder.addConclusion("Happy Coding!");

// Return the document and depending on the concrete
// implementation of the DocumentBuilder, we could return
// either a pdf or html document.
return documentBuilder.buildDocument();

}

上面的方法可以出现在控制器代码或客户端代码中,并且可以通过更改传递给该方法的DocumentBuilder的具体类型来构建不同的文档类型。我们可以从抽象类中得到一个HtmlDocumentBuilder和一个PdfDocumentBuilder派生类DocumentBuilder。

抽象工厂模式与建造者模式

  • 建造者模式可能看起来类似于抽象工厂模式,但是二者还是略有不同。其中一个不同之处在于,建造者模式是逐步创建对象的,而抽象工厂模式则一步就返回了对象。

建造者模式的比喻

假设我们有一座工厂吧,工厂得有基础核心部门(Builder),这个部门拥有所有的基础生产资料模板,然后我们需要几个分别生产不同产品的车间(Concrete Builder)吧,这些车间需要自己从基础核心部门挑选合适的模板然后自己填充定制,用于生产自己的产品。 工厂的生产不能太随意,得需要指挥部(Director)指导,指挥部在基础核心部门(Builder)等着其他车间(Concrete Builder)来接受指导,给出相应的指挥命令(也就是参数),最终车间(Concrete Builder)生产出合适的产品。

参考资料

设计模式总结
Software Design Patterns: Best Practices for Software Developers /github educative

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×