服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - 深入解析C#设计模式编程中对建造者模式的运用

深入解析C#设计模式编程中对建造者模式的运用

2021-11-12 15:18LearningHard C#

这篇文章主要介绍了C#设计模式编程中对建造者模式的运用,文中还介绍了在.NET框架下建造者模式编写思路的实现,需要的朋友可以参考下

示例

我们先来以这样一个场景引入: 
在电脑城装机总有这样的经历。我们到了店里,先会有一个销售人员来询问你希望装的机器是怎么样的配置,他会给你一些建议,最终会形成一张装机单。和客户确定了装机配置以后,他会把这张单字交给提货的人,由他来准备这些配件,准备完成后交给装机技术人员。技术人员会把这些配件装成一个整机交给客户。

不管是什么电脑,它总是由CPU、内存、主板、硬盘以及显卡等部件构成的,并且装机的过程总是固定的:

  • 把主板固定在机箱中
  • 把CPU安装到主板上
  • 把内存安装到主板上
  • 把硬盘连接到主板上
  • 把显卡安装到主板上

但是,每台兼容机的部件都各不相同的,有些配置高一点,有些配置低一点,这是变化点。对于装机技术人员来说,他不需要考虑这些配件从哪里来的,他只需要把他们组装在一起了,这是稳定的装机流程。要把这种变化的配件和稳定的流程进行分离就需要引入Builder模式。
示例代码

?
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
using System;
 
using System.Collections.Generic;
 
using System.Text;
 
using System.Reflection;
 
 
 
namespace BuilderExemple
 
{
 
  classProgram
 
  {
 
    staticvoid Main(string[] args)
 
    {
 
      ComputerFactory factory = newComputerFactory();
 
      ComputerBuilder office = newOfficeComputerBuilder();
 
      factory.BuildComputer(office);
 
      office.Computer.ShowSystemInfo();
 
      ComputerBuilder game = newGameComputerBuilder();
 
      factory.BuildComputer(game);
 
      game.Computer.ShowSystemInfo();
 
    }
 
  }
 
 
 
  classComputerFactory
 
  {
 
    publicvoid BuildComputer(ComputerBuilder cb)
 
    {
 
      Console.WriteLine();
 
      Console.WriteLine(">>>>>>>>>>>>>>>>>>Start Building " + cb.Name);
 
      cb.SetupMainboard();
 
      cb.SetupCpu();
 
      cb.SetupMemory();
 
      cb.SetupHarddisk();
 
      cb.SetupVideocard();
 
      Console.WriteLine(">>>>>>>>>>>>>>>>>>Build " + cb.Name + " Completed");
 
      Console.WriteLine();
 
    }
 
  }
 
 
 
  abstractclassComputerBuilder
 
  {
 
    protectedstring name;
 
 
 
    publicstring Name
 
    {
 
      get { return name; }
 
      set { name = value; }
 
    }
 
 
 
    protectedComputer computer;
 
 
 
    publicComputer Computer
 
    {
 
      get { return computer; }
 
      set { computer = value; }
 
    }
 
 
 
    public ComputerBuilder()
 
    {
 
      computer = newComputer();
 
    }
 
 
 
    publicabstractvoid SetupMainboard();
 
    publicabstractvoid SetupCpu();
 
    publicabstractvoid SetupMemory();
 
    publicabstractvoid SetupHarddisk();
 
    publicabstractvoid SetupVideocard();
 
  }
 
 
 
  classOfficeComputerBuilder : ComputerBuilder
 
  {
 
    public OfficeComputerBuilder()
 
    {
 
      name = "OfficeComputer";
 
    }
 
 
 
    publicoverridevoid SetupMainboard()
 
    {
 
      computer.Mainboard = "Abit升技LG-95C 主板(Intel 945GC芯片组/LGA 775/1066MHz) ";
 
    }
 
 
 
    publicoverridevoid SetupCpu()
 
    {
 
      computer.Cpu = "Intel 英特尔赛扬D 336 (2.8GHz/LGA 775/256K/533MHz) ";
 
    }
 
 
 
    publicoverridevoid SetupMemory()
 
    {
 
      computer.Memory = "Patriot博帝DDR2 667 512MB 台式机内存";
 
    }
 
 
 
    publicoverridevoid SetupHarddisk()
 
    {
 
      computer.Harddisk = "Hitachi日立SATAII接口台式机硬盘(80G/7200转/8M)盒装";
 
    }
 
 
 
    publicoverridevoid SetupVideocard()
 
    {
 
      computer.Videocard = "主板集成";
 
    }
 
  }
 
 
 
  classGameComputerBuilder : ComputerBuilder
 
  {
 
    public GameComputerBuilder()
 
    {
 
      name = "GameComputer";
 
    }
 
 
 
    publicoverridevoid SetupMainboard()
 
    {
 
      computer.Mainboard = "GIGABYTE技嘉GA-965P-DS3 3.3 主板(INTEL P965 东莞产)" ;
 
    }
 
 
 
    publicoverridevoid SetupCpu()
 
    {
 
      computer.Cpu = "Intel 英特尔酷睿E4400 (2.0GHz/LGA 775/2M/800MHz)盒装";
 
    }
 
 
 
    publicoverridevoid SetupMemory()
 
    {
 
      computer.Memory = "G.SKILL 芝奇F2-6400CL5D-2GBNQ DDR2 800 1G*2台式机内存";
 
    }
 
 
 
    publicoverridevoid SetupHarddisk()
 
    {
 
      computer.Harddisk = "Hitachi日立SATAII接口台式机硬盘(250G/7200转/8M)盒装";
 
    }
 
 
 
    publicoverridevoid SetupVideocard()
 
    {
 
      computer.Videocard = "七彩虹逸彩GT-GD3 UP烈焰战神H10 显卡(GeForce 8600GT/256M/DDR3)支持HDMI!";
 
    }
 
  }
 
 
 
  classComputer
 
  {
 
    privatestring videocard;
 
 
 
    publicstring Videocard
 
    {
 
      get { return videocard; }
 
      set { videocard = value; }
 
    }
 
 
 
    privatestring cpu;
 
 
 
    publicstring Cpu
 
    {
 
      get { return cpu; }
 
      set { cpu = value; }
 
    }
 
 
 
    privatestring mainboard;
 
 
 
    publicstring Mainboard
 
    {
 
      get { return mainboard; }
 
      set { mainboard = value; }
 
    }
 
 
 
    privatestring memory;
 
 
 
    publicstring Memory
 
    {
 
      get { return memory; }
 
      set { memory = value; }
 
    }
 
 
 
    privatestring harddisk;
 
 
 
    publicstring Harddisk
 
    {
 
      get { return harddisk; }
 
      set { harddisk = value; }
 
    }
 
 
 
    publicvoid ShowSystemInfo()
 
    {
 
      Console.WriteLine("==================SystemInfo==================");
 
      Console.WriteLine("CPU:" + cpu);
 
      Console.WriteLine("MainBoard:" + mainboard);
 
      Console.WriteLine("Memory:" + memory);
 
      Console.WriteLine("VideoCard:" + videocard);
 
      Console.WriteLine("HardDisk:" + harddisk);
 
    }
 
  }
 
}

代码说明: 

ComputerFactory是建造者模式的指导者。指导者做的是稳定的建造工作,假设它就是一个技术人员,他只是在做按照固定的流程,把配件组装成计算机的重复劳动工作。他不知道他现在组装的是一台游戏电脑还是一台办公用电脑,他也不知道他往主板上安装的内存是1G还是2G的。呵呵,看来是不称职的技术人员。

ComputerBuilder是抽象建造者角色。它主要是用来定义两种接口,一种接口用于规范产品的各个部分的组成。比如,这里就规定了组装一台电脑所需要的5个工序。第二种接口用于返回建造后的产品,在这里我们没有定义抽象方法,反正建造出来的总是电脑。

OfficeComputerBuilder和GameComputerBuilder是具体的建造者。他的工作就是实现各建造步骤的接口,以及实现返回产品的接口,在这里后者省略了。

Computer就是建造出来的复杂产品。在代码中,我们的各种建造步骤都是为创建产品中的各种配件服务的,Computer定义了一个相对具体的产品,在应用中可以把这个产品进行比较高度的抽象,使得不同的具体建造者甚至可以建造出完全不同的产品。

看看客户端的代码,用户先是选择了一个具体的Builder,用户应该很明确它需要游戏电脑还是办公电脑,但是它可以对电脑一无所知,由销售人员给出一个合理的配置单。然后用户让ComputerFactory去为它组装这个电脑。组装完成后ComputerFactory开机,给用户验收电脑的配置是否正确。

你或许觉得ComputerBuilder和是抽象工厂模式中的抽象工厂角色差不多,GameComputerBuilder又像是具体工厂。其实,建造者模式和抽象工厂模式的侧重点不同,前者强调一个组装的概念,一个复杂对象由多个零件组装而成并且组装是按照一定的标准射顺序进行的,而后者强调的是创建一系列产品。建造者模式适用于组装一台电脑,而抽象工厂模式适用于提供用户笔记本电脑、台式电脑和掌上电脑的产品系列。


建造者模式的定义和类图
  介绍完了建造者模式的具体实现之后吗,下面具体看下建造者模式的具体定义是怎样的。

建造者模式(Builder Pattern):将一个复杂对象的构建于它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式使得建造代码与表示代码的分离,可以使客户端不必知道产品内部组成的细节,从而降低了客户端与具体产品之间的耦合度,下面通过类图来帮助大家更好地理清建造者模式中类之间的关系。

深入解析C#设计模式编程中对建造者模式的运用

 

建造者模式的分析
介绍完了建造者模式的具体实现之后,让我们总结下建造模式的实现要点:

在建造者模式中,指挥者是直接与客户端打交道的,指挥者将客户端创建产品的请求划分为对各个部件的建造请求,再将这些请求委派到具体建造者角色,具体建造者角色是完成具体产品的构建工作的,却不为客户所知道。
建造者模式主要用于“分步骤来构建一个复杂的对象”,其中“分步骤”是一个固定的组合过程,而复杂对象的各个部分是经常变化的(也就是说电脑的内部组件是经常变化的,这里指的的变化如硬盘的大小变了,CPU由单核变双核等)。
产品不需要抽象类,由于建造模式的创建出来的最终产品可能差异很大,所以不大可能提炼出一个抽象产品类。
在前面文章中介绍的抽象工厂模式解决了“系列产品”的需求变化,而建造者模式解决的是 “产品部分” 的需要变化。
由于建造者隐藏了具体产品的组装过程,所以要改变一个产品的内部表示,只需要再实现一个具体的建造者就可以了,从而能很好地应对产品组成组件的需求变化。

.NET 中建造者模式的实现
  前面的设计模式在.NET类库中都有相应的实现,那在.NET 类库中,是否也存在建造者模式的实现呢? 然而对于疑问的答案是肯定的,在.NET 类库中,System.Text.StringBuilder(存在mscorlib.dll程序集中)就是一个建造者模式的实现。不过它的实现属于建造者模式的演化,此时的建造者模式没有指挥者角色和抽象建造者角色,StringBuilder类即扮演着具体建造者的角色,也同时扮演了指挥者和抽象建造者的角色,此时建造模式的实现如下:

?
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
/// <summary>
  /// 建造者模式的演变
  /// 省略了指挥者角色和抽象建造者角色
  /// 此时具体建造者角色扮演了指挥者和建造者两个角色
  /// </summary>
  public class Builder
  {
    // 具体建造者角色的代码
    private Product product = new Product();
    public void BuildPartA()
    {
      product.Add("PartA");
    }
    public void BuildPartB()
    {
      product.Add("PartB");
    }
    public Product GetProduct()
    {
      return product;
    }
    // 指挥者角色的代码
    public void Construct()
    {
      BuildPartA();
      BuildPartB();
    }
  }
  /// <summary>
  /// 产品类
  /// </summary>
  public class Product
  {
    // 产品组件集合
    private IList<string> parts = new List<string>();
    // 把单个组件添加到产品组件集合中
    public void Add(string part)
    {
      parts.Add(part);
    }
    public void Show()
    {
      Console.WriteLine("产品开始在组装.......");
      foreach (string part in parts)
      {
        Console.WriteLine("组件" + part + "已装好");
      }
      Console.WriteLine("产品组装完成");
    }
  }
  // 此时客户端也要做相应调整
  class Client
  {
    private static Builder builder;
    static void Main(string[] args)
    {
      builder = new Builder();
      builder.Construct();
      Product product = builder.GetProduct();
      product.Show();
      Console.Read();
    }
  }

StringBuilder类扮演着建造string对象的具体建造者角色,其中的ToString()方法用来返回具体产品给客户端(相当于上面代码中GetProduct方法)。其中Append方法用来创建产品的组件(相当于上面代码中BuildPartA和BuildPartB方法),因为string对象中每个组件都是字符,所以也就不需要指挥者的角色的代码(指的是Construct方法,用来调用创建每个组件的方法来完成整个产品的组装),因为string字符串对象中每个组件都是一样的,都是字符,所以Append方法也充当了指挥者Construct方法的作用。


总结
到这里,建造者模式的介绍就结束了,建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使的同样的构建过程可以创建不同的表示。建造者模式的本质是使组装过程(用指挥者类进行封装,从而达到解耦的目的)和创建具体产品解耦,使我们不用去关心每个组件是如何组装的。

延伸 · 阅读

精彩推荐
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21