在软件系统中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成。

例如一个采购系统中,如果需要采购员去采购一批电脑时,在这个实际需求中,电脑就是一个复杂的对象,它是由CPU、主板、硬盘、显卡、机箱等组装而成的,如果此时让采购员一台一台电脑去组装的话真是要累死采购员了,这里就可以采用建造者模式来解决这个问题,我们可以把电脑的各个组件的组装过程封装到一个建造者类对象里,建造者只要负责返还给客户端全部组件都建造完毕的产品对象就可以了。然而现实生活中也是如此的,如果公司要采购一批电脑,此时采购员不可能自己去买各个组件并把它们组织起来,此时采购员只需要像电脑城的老板说自己要采购什么样的电脑就可以了,电脑城老板自然会把组装好的电脑送到公司。

一、 建造者(Builder)模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

建造者模式使得建造代码与表示代码的分离,可以使客户端不必知道产品内部组成的细节,从而降低了客户端与具体产品之间的耦合度。

二、 建造者模式的类图

三、 建造者模式的实现

在这个例子中,电脑城的老板是直接与客户(也就是指采购员)联系的,然而电脑的组装是由老板指挥装机人员去把电脑的各个部件组装起来,真真负责创建产品(这里产品指的就是电脑)的人就是电脑城的装机人员。理清了这个逻辑过程之后,下面就具体看下如何用代码来表示这种现实生活中的逻辑过程:

  1. using System;
  2. using System.Collections.Generic;
  3.  
  4. /// <summary>
  5. /// 以组装电脑为例子
  6. /// 每台电脑的组成过程都是一致的,但是使用同样的构建过程可以创建不同的表示(即可以组装成不一样的电脑,配置不一样)
  7. /// 组装电脑的这个场景就可以应用建造者模式来设计
  8. /// </summary>
  9. namespace BuilderPattern
  10. {
  11. /// <summary>
  12. /// 客户类
  13. /// </summary>
  14. class Customer
  15. {
  16. static void Main(string[] args)
  17. {
  18. // 客户找到电脑城老板说要买电脑,这里要装两台电脑
  19. // 创建指挥者和构造者
  20. var director = new Director();
  21. Builder b1 = new ConcreteBuilder1();
  22. Builder b2 = new ConcreteBuilder2();
  23.  
  24. // 老板叫员工去组装第一台电脑
  25. director.Construct(b1);
  26.  
  27. // 组装完,组装人员搬来组装好的电脑
  28. Computer computer1 = b1.GetComputer();
  29. computer1.Show();
  30.  
  31. // 老板叫员工去组装第二台电脑
  32. director.Construct(b2);
  33. Computer computer2 = b2.GetComputer();
  34. computer2.Show();
  35.  
  36. Console.Read();
  37. }
  38. }
  39.  
  40. /// <summary>
  41. /// 小王和小李难道会自愿地去组装嘛,谁不想休息的,这必须有一个人叫他们去组装才会去的
  42. /// 这个人当然就是老板了,也就是建造者模式中的指挥者
  43. /// 指挥创建过程类
  44. /// </summary>
  45. public class Director
  46. {
  47. // 组装电脑
  48. public void Construct(Builder builder)
  49. {
  50. builder.BuildPartCpu();
  51. builder.BuildPartMainBoard();
  52. }
  53. }
  54.  
  55. /// <summary>
  56. /// 电脑类
  57. /// </summary>
  58. public class Computer
  59. {
  60. // 电脑组件集合
  61. private readonly IList<string> _parts = new List<string>();
  62.  
  63. // 把单个组件添加到电脑组件集合中
  64. public void Add(string part)
  65. {
  66. _parts.Add(part);
  67. }
  68.  
  69. public void Show()
  70. {
  71. Console.WriteLine("电脑开始在组装.......");
  72. foreach (string part in _parts)
  73. {
  74. Console.WriteLine("组件" + part + "已装好");
  75. }
  76.  
  77. Console.WriteLine("电脑组装好了");
  78. }
  79. }
  80.  
  81. /// <summary>
  82. /// 抽象建造者,这个场景下为 "组装人" ,这里也可以定义为接口
  83. /// </summary>
  84. public abstract class Builder
  85. {
  86. // 装CPU
  87. public abstract void BuildPartCpu();
  88. // 装主板
  89. public abstract void BuildPartMainBoard();
  90.  
  91. // 当然还有装硬盘,电源等组件,这里省略
  92.  
  93. // 获得组装好的电脑
  94. public abstract Computer GetComputer();
  95. }
  96.  
  97. /// <summary>
  98. /// 具体创建者,具体的某个人为具体创建者,例如:装机小王啊
  99. /// </summary>
  100. public class ConcreteBuilder1 : Builder
  101. {
  102. readonly Computer _computer = new Computer();
  103. public override void BuildPartCpu()
  104. {
  105. _computer.Add("CPU1");
  106. }
  107.  
  108. public override void BuildPartMainBoard()
  109. {
  110. _computer.Add("Main board1");
  111. }
  112.  
  113. public override Computer GetComputer()
  114. {
  115. return _computer;
  116. }
  117. }
  118.  
  119. /// <summary>
  120. /// 具体创建者,具体的某个人为具体创建者,例如:装机小李啊
  121. /// 又装另一台电脑了
  122. /// </summary>
  123. public class ConcreteBuilder2 : Builder
  124. {
  125. readonly Computer _computer = new Computer();
  126. public override void BuildPartCpu()
  127. {
  128. _computer.Add("CPU2");
  129. }
  130.  
  131. public override void BuildPartMainBoard()
  132. {
  133. _computer.Add("Main board2");
  134. }
  135.  
  136. public override Computer GetComputer()
  137. {
  138. return _computer;
  139. }
  140. }
  141. }

上面代码中都有详细的注释代码,这里就不过多解释,大家可以参考代码和注释来与现实生活中的例子做对比,下图展示了上面代码的运行结果:

  1. 电脑开始在组装……
  2. 组件CPU1已装好
  3. 组件Main board1已装好
  4. 电脑组装好了
  5. 电脑开始在组装……
  6. 组件CPU2已装好
  7. 组件Main board2已装好
  8. 电脑组装好了

四、 .NET 中建造者模式的实现

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

  1. using System;
  2. using System.Collections.Generic;
  3.  
  4. /// <summary>
  5. /// 建造者模式的演变
  6. /// 省略了指挥者角色和抽象建造者角色
  7. /// 此时具体建造者角色扮演了指挥者和建造者两个角色
  8. /// </summary>
  9. public class Builder
  10. {
  11. // 具体建造者角色的代码
  12. private readonly Product _product = new Product();
  13. public void BuildPartA()
  14. {
  15. _product.Add("PartA");
  16. }
  17. public void BuildPartB()
  18. {
  19. _product.Add("PartB");
  20. }
  21. public Product GetProduct()
  22. {
  23. return _product;
  24. }
  25. // 指挥者角色的代码
  26. public void Construct()
  27. {
  28. BuildPartA();
  29. BuildPartB();
  30. }
  31. }
  32.  
  33. /// <summary>
  34. /// 产品类
  35. /// </summary>
  36. public class Product
  37. {
  38. // 产品组件集合
  39. private readonly IList<string> _parts = new List<string>();
  40.  
  41. // 把单个组件添加到产品组件集合中
  42. public void Add(string part)
  43. {
  44. _parts.Add(part);
  45. }
  46.  
  47. public void Show()
  48. {
  49. Console.WriteLine("产品开始在组装.......");
  50. foreach (string part in _parts)
  51. {
  52. Console.WriteLine("组件" + part + "已装好");
  53. }
  54.  
  55. Console.WriteLine("产品组装完成");
  56. }
  57. }
  58.  
  59. // 此时客户端也要做相应调整
  60. class Client
  61. {
  62. private static Builder _builder;
  63. static void Main(string[] args)
  64. {
  65. _builder = new Builder();
  66. _builder.Construct();
  67. Product product = _builder.GetProduct();
  68. product.Show();
  69. Console.Read();
  70. }
  71. }

五、 建造者模式的分析

建造模式的实现要点:

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

六、 总结

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

C#设计模式-建造者模式的更多相关文章

  1. 3.java设计模式-建造者模式

    Java设计模式-建造者模式 在<JAVA与模式>一书中开头是这样描述建造(Builder)模式的: 建造模式是对象的创建模式.建造模式可以将一个产品的内部表象(internal repr ...

  2. 设计模式—建造者模式(Builder)

    title: 设计模式-建造者模式 建造者模式(Builder)是一步一步创建一个复杂的对象,它允许用户只通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的具体构建细节.建造者模式属于对 ...

  3. [Head First设计模式]山西面馆中的设计模式——建造者模式

    系列文章 [Head First设计模式]山西面馆中的设计模式——装饰者模式 [Head First设计模式]山西面馆中的设计模式——观察者模式 引言 将学习融入生活中,是件很happy的事情,不会感 ...

  4. 说说设计模式~建造者模式(Builder)

    返回目录 建造者模式是我的"设计模式"里创建型模式里的最后一篇,这种模式在实现中,很多架构都用到了,如MVC,MVP,MVVM,它们都是有建造者模式的精髓的,即,创建与表现分享,我 ...

  5. C++设计模式——建造者模式

    建造者模式 在GOF的<设计模式 可复用面向对象软件的基础>中是这样说的:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 这句话,似懂非懂的.一个复杂对象的创建 ...

  6. Python设计模式——建造者模式

    需求,画人物,要求画一个人的头,左手,右手,左脚,右脚和身体,画一个瘦子,一个胖子 不使用设计模式 #encoding=utf-8 __author__ = 'kevinlu1010@qq.com' ...

  7. 我的Java设计模式-建造者模式

    在未上大学之前,一直有个梦想"I have a dream!",就是能成为一位汽车工程师,一直幻想着开着自己设计的汽车飞奔在公路上,迷倒了万千少女.咳咳~~虽然现在没实现我的dre ...

  8. 设计模式 | 建造者模式/生成器模式(builder)

    定义: 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示. 结构:(书中图,侵删) 一个产品类 一个指定产品各个部件的抽象创建接口 若干个实现了各个部件的具体实现的创建类 一个 ...

  9. java设计模式——建造者模式

    一. 定义与类型 定义:将一个复杂对象的构建与它的表示分离,使用同样的构建过程可以创建不同的表示 用户只需制定需要建造的类型就可以得到它们,建造过程以及细节不需要知道 类型:创建型 建造者模式与工厂模 ...

随机推荐

  1. c语言结构体

    [C语言]21-结构体 本文目录 一.什么是结构体 二.结构体的定义 三.结构体变量的定义 四.结构体的注意点 五.结构体的初始化 六.结构体的使用 七.结构体数组 八.结构体作为函数参数 九.指向结 ...

  2. kafka的log存储解析——topic的分区partition分段segment以及索引等

    转自:http://blog.csdn.net/jewes/article/details/42970799 引言 Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相 ...

  3. Android 单元测试(junit、mockito、robolectric)

    1.运用JUnit4 进行单元测试 首先在工程的 src 文件夹内创建 test 和 test/java 文件夹. 打开工程的 build.gradle(Module:app)文件,添加JUnit4依 ...

  4. Linux 时间时区同步

    $ sudo cp /usr/share/zoneinfo/Asia/ShangHai /etc/localtime 上述是修改系统时区 同步时间 1.  安装ntpdate工具 $ sudo apt ...

  5. Qt里怎么处理二进制数据

    Qt里有个专门的类QDataStream就是专门读写二进制数据的, 它与QByteArray搭配在网络编程中有奇效. 来个栗子: // write data QByteArray data; QDat ...

  6. iOS 中 ARC 项目 兼容 MRC

    iOS 项目中MRC 和 ARC 项目的代码兼容问题: 1.ARC 项目中导入 MRC 第三方类的时候要在此类上添加 -objc-arc. 2.MRC 项目中导入 ARC 类的时候要在次类上添加 -f ...

  7. AmazeUI 框架知识点-元素

    1.按钮  .am-btn 圆角按钮 .am-radius 椭圆形按钮 .am-round 按钮激活状态 .am-active 禁用状态 .am-disabled 2.按钮尺寸.am-btn-xl . ...

  8. NFSv4的引用,迁移和备份(用户手册 v0.2)

    RFC3530 定义了NFS文件系统迁移和引用的管理机制.文件系统定位功能通过fs_location属性向客户端提供文件系统的位置信息.fs_location属 性是一个包含有位置信息的列表,位置信息 ...

  9. PHP 通过百度API 实现通过城市名称获取经度

    $city = $_GET['city'];print_r(getjw($city));/*** $city 需要查询的地址* $key 百度开发者账号*/function getjw($city){ ...

  10. USACO翻译:USACO 2012 FEB Silver三题

    USACO 2012 FEB SILVER 一.题目概览 中文题目名称 矩形草地 奶牛IDs 搬家 英文题目名称 planting cowids relocate 可执行文件名 planting co ...