随笔 - 68  文章 - 0 评论 - 41 阅读 - 56万
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

下面是我自己对建造者模式的理解。具体我还没在开发中应用过,这只是对于书本的理解。

建造者模式

建造者模式:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。这是建造者模式的标准表达,不过看着让人迷惑,什么叫构建和表示的分离?一个对象使用构造函数构造之后不就固定了,只有通过它方法来改变它的属性吗?而且还要同样的构建过程搞出不同的表示,怎么可能呢?多写几个构造函数?

其实多写几个构造函数,根据不同参数设置对象不同的属性,也可以达到这样的效果,只是这样就非常麻烦了,每次要增加一种表示就要添加一个构造函数,将来构造函数会多得连自己都不记得了,这违背了开放-封闭的原则。

要不就只能设计几个set函数,每次属性不一样了,我就构造一个对象,然后用set函数改变对象的属性。这样也可以达到效果。只是代码就会非常冗余了,每个要用到这个对象的地方,都要写上好几句语句,一旦对象有点什么变化,还得到处都改一遍,这样就很容易出错,以后别人看着这种神逻辑和神代码估计也会崩溃了。而且这也违背了依赖倒转的原则。

于是大神们就开始想了,不能加很多构造函数,也不能直接用一堆set函数,然后发现,有些对象的构建是固定的几个步骤的,就像一条流水线一样,任何的产品都是通过每一个固定的步骤拼凑出来的。例如说一部手机,先放主板,再放屏幕,再放电池,再放外壳,贴个膜就能卖几千了,每次推出新产品,就换个更好的主板,换个大点的屏幕,再整个大容量电池,贴个超牛B的高透膜,又能卖出个新价钱。就是说,这些步骤都没有变,变的只是每个部分的东西。

这就是大神的厉害之处了,透过现象看本质,基本有变的,有不变的,那敢情好,面向对象的一个重要指导思想就是,封装隔离变化的,留出不变的。于是他们就用一个Builder类把步骤中的每个部分封装起来,这个类的主要作用就是生产每个部件,再抽象一下提升高度,这样就依赖倒转了,这样每次只需要添加一个类,这个类还是这几个部分,只是内部的实现已经不一样了,这样就满足了开放-封闭的原则了。但还是有一个问题,光有Builder类还不行,虽然产品的每个部分都有对应的函数,但是用起来的话,还是跟前面说的set函数一样,一用就要使用一大堆函数,也就是这变的东西是封装起来了,但这不变的东西还没留出来。这时,就添加一个Director类,这个类就是专门规定组装产品的步骤的,这样只要告诉Director使用哪个Builder,就能生产出不同的产品,对于客户端来说,只看到用了Director的一个construct函数,甚是方便。

再反过来看建造者模式的定义,构建指的就是生产一个产品的步骤,表示就是每个产品部分的具体实现,通过Director封装步骤,通过Builder封装产品部分的实现,再把他两隔离开,就能隔离变的,留出不变的供客户端使用。

image

图中可以看到,Product是必须要知道,没有抽象,但是这个产品却可以由不同的部分组合而成。Director里的construct也是固定,没有抽象出来,如果要更改步骤,也要添加一个函数,或者再添一个Diector,所以建造者模式一般应用于步骤不会发生大的变化,而产品会发生大变化的情况。

 

常用的场景

C#中的StringBuilder就是一个建造者的例子,但只是一个建造者,还缺一个Director,不能算一个完整的建造者模式。建造者模式一般应用于构建产品的步骤(也可以称为算法)不变,而每个步骤的具体实现又剧烈变化的情况。

 

优点

1.隔离了构建的步骤和具体的实现,为产品的具体实现提供了灵活度。

2.封装和抽象了每个步骤的实现,实现了依赖倒转原则。

3.封装了具体的步骤,减少了代码的冗余。

 

缺点

1.要求构建产品的步骤(算法)是不能剧烈变化的,最好是不变的,这样就影响了灵活度。

 

C++实现代码

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
Builder.h
 
#ifndef _BUILDER_H_
#define _BUILDER_H_
 
#include <stdio.h>
 
class Product{
public:
    Product();
    ~Product();
 
    void setPartA(int param);
    void setPartB(int param);
    void setPartC(int param);
 
    void show();
 
private:
    int partA;
    int partB;
    int partC;
 
};
 
 
class AbstractBuilder{
public:
    AbstractBuilder();
    virtual ~AbstractBuilder();
 
    virtual void createProduct() = 0;
    virtual void buildPartA(int param) = 0;
    virtual void buildPartB(int param) = 0;
    virtual void buildPartC(int param) = 0;
 
    virtual Product* getProduct() = 0;
};
 
 
class Builder: public AbstractBuilder{
public:
    Builder();
    ~Builder();
 
    void createProduct();
    void buildPartA(int param);
    void buildPartB(int param);
    void buildPartC(int param);
 
    Product* getProduct();
 
private:
    Product* curProduct;
 
};
 
#endif

 

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
95
96
97
98
99
100
Builder.cpp
 
#include "Builder.h"
 
 
 
Product::Product()
{
 
}
 
 
Product::~Product()
{
 
}
 
 
void Product::setPartA(int param)
{
    partA = param;
}
 
 
void Product::setPartB(int param)
{
    partB = param;
}
 
 
void Product::setPartC(int param)
{
    partC = param;
}
 
 
void Product::show()
{
    fprintf(stderr,"partA = %d  partB = %d  partC = %d\n",partA,partB,partC);
}
 
 
AbstractBuilder::AbstractBuilder()
{
 
}
 
 
AbstractBuilder::~AbstractBuilder()
{
 
}
 
 
Builder::Builder()
:curProduct(NULL)
{
 
}
 
 
Builder::~Builder()
{
 
}
 
 
void Builder::createProduct()
{
    fprintf(stderr,"创建一个产品空壳\n");
    curProduct = new Product();
}
 
 
void Builder::buildPartA(int param)
{
    fprintf(stderr,"正在构建产品的A部分\n");
    curProduct->setPartA(param);
}
 
 
void Builder::buildPartB(int param)
{
    fprintf(stderr,"正在构建产品的B部分\n");
    curProduct->setPartB(param);
}
 
 
void Builder::buildPartC(int param)
{
    fprintf(stderr,"正在构建产品的C部分\n");
    curProduct->setPartC(param);
}
 
 
Product* Builder::getProduct()
{
    //我的理解就是产品交出去之后,怎么释放怎么弄就不归建造者管了
    return curProduct;
}

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Director.h
 
#ifndef _DIRECTOR_H_
#define _DIRECTOR_H_
 
#include "Builder.h"
 
class Director
{
public:
    Director(AbstractBuilder* builder);
    ~Director();
 
    void construct();
 
private:
    AbstractBuilder* curBuilder;
};
 
#endif

 

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
Director.cpp
 
 
#include "Director.h"
 
 
Director::Director(AbstractBuilder* builder)
{
    curBuilder = builder;
}
 
 
Director::~Director()
{
 
}
 
 
void Director::construct()
{
    if (!curBuilder)
        return;
 
    curBuilder->createProduct();
    curBuilder->buildPartA(1);
    curBuilder->buildPartB(2);
    curBuilder->buildPartC(3);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
client.cpp
 
#include "Director.h"
 
 
 
int main()
{
    AbstractBuilder* builder = new Builder();
    Director* director = new Director(builder);
 
    director->construct();
     
    Product* product = builder->getProduct();
    product->show();
    return 0;
}

 

1
g++ -o client client.cpp Builder.cpp Director.cpp

 

运行结果

image

posted on   曾经的你|  阅读(9740)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
点击右上角即可分享
微信分享提示