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

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

一、 建造者(Builder)模式

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

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

二、 建造者模式的类图

三、 建造者模式的实现

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

using System;
using System.Collections.Generic; /// <summary>
/// 以组装电脑为例子
/// 每台电脑的组成过程都是一致的,但是使用同样的构建过程可以创建不同的表示(即可以组装成不一样的电脑,配置不一样)
/// 组装电脑的这个场景就可以应用建造者模式来设计
/// </summary>
namespace BuilderPattern
{
/// <summary>
/// 客户类
/// </summary>
class Customer
{
static void Main(string[] args)
{
// 客户找到电脑城老板说要买电脑,这里要装两台电脑
// 创建指挥者和构造者
var director = new Director();
Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2(); // 老板叫员工去组装第一台电脑
director.Construct(b1); // 组装完,组装人员搬来组装好的电脑
Computer computer1 = b1.GetComputer();
computer1.Show(); // 老板叫员工去组装第二台电脑
director.Construct(b2);
Computer computer2 = b2.GetComputer();
computer2.Show(); Console.Read();
}
} /// <summary>
/// 小王和小李难道会自愿地去组装嘛,谁不想休息的,这必须有一个人叫他们去组装才会去的
/// 这个人当然就是老板了,也就是建造者模式中的指挥者
/// 指挥创建过程类
/// </summary>
public class Director
{
// 组装电脑
public void Construct(Builder builder)
{
builder.BuildPartCpu();
builder.BuildPartMainBoard();
}
} /// <summary>
/// 电脑类
/// </summary>
public class Computer
{
// 电脑组件集合
private readonly 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("电脑组装好了");
}
} /// <summary>
/// 抽象建造者,这个场景下为 "组装人" ,这里也可以定义为接口
/// </summary>
public abstract class Builder
{
// 装CPU
public abstract void BuildPartCpu();
// 装主板
public abstract void BuildPartMainBoard(); // 当然还有装硬盘,电源等组件,这里省略 // 获得组装好的电脑
public abstract Computer GetComputer();
} /// <summary>
/// 具体创建者,具体的某个人为具体创建者,例如:装机小王啊
/// </summary>
public class ConcreteBuilder1 : Builder
{
readonly Computer _computer = new Computer();
public override void BuildPartCpu()
{
_computer.Add("CPU1");
} public override void BuildPartMainBoard()
{
_computer.Add("Main board1");
} public override Computer GetComputer()
{
return _computer;
}
} /// <summary>
/// 具体创建者,具体的某个人为具体创建者,例如:装机小李啊
/// 又装另一台电脑了
/// </summary>
public class ConcreteBuilder2 : Builder
{
readonly Computer _computer = new Computer();
public override void BuildPartCpu()
{
_computer.Add("CPU2");
} public override void BuildPartMainBoard()
{
_computer.Add("Main board2");
} public override Computer GetComputer()
{
return _computer;
}
}
}

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

电脑开始在组装……
组件CPU1已装好
组件Main board1已装好
电脑组装好了
电脑开始在组装……
组件CPU2已装好
组件Main board2已装好
电脑组装好了

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

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

using System;
using System.Collections.Generic; /// <summary>
/// 建造者模式的演变
/// 省略了指挥者角色和抽象建造者角色
/// 此时具体建造者角色扮演了指挥者和建造者两个角色
/// </summary>
public class Builder
{
// 具体建造者角色的代码
private readonly 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 readonly 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();
}
}

五、 建造者模式的分析

建造模式的实现要点:

  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. MyEclipse创建Maven工程

    先要在MyEclipse中对Maven进行设置:

  2. ASP.NET MVC3中Controller与View之间的数据传递总结

    一.  Controller向View传递数据 1.       使用ViewData传递数据 我们在Controller中定义如下: ViewData["Message_ViewData& ...

  3. dedecms 文章页图片改为绝对路径

    这几天在网站改版,想把网站做大,想做频道页二级域名,于是在做网站的过程中发现一个问题,dedecms开设二级域名后,在二级域名的文章页无法显示图片,查看源代码后发现问题,由于dedecms文章页中的图 ...

  4. Sprint2团队贡献分

    团队贡献分: 郭志豪:31%  http://www.cnblogs.com/gzh13692021053/ 杨子健:22%http://www.cnblogs.com/yzj666/ 谭宇森:23% ...

  5. 解决svn uuid变更问题

    简介: 今天在snv根目录下重新定位上传的url,更改后出现如下错误 .可以看到,原来Repository创建者的uuid是前者,而现在我操作的是后者的uuid.因此,目前的操作办法是 使用相关命令更 ...

  6. java合并pdf

    一.开发准备 下载pdfbox-app-1.7.1.jar包;下载地址:http://download.csdn.net/detail/yanning1314/4852276 二.简单小例子 在开发中 ...

  7. js中使用new Date(str)创建时间对象不兼容firefox和ie的解决方式

    /** * 解决 ie,火狐浏览器不兼容new Date(s) * @param strDate * 返回 date对象 * add by zyf at 2015年11月5日 */ function ...

  8. 回流(reflow)与重绘(repaint)

    最近项目排期不紧,于是看了一下之前看了好久也没看明白的chrome调试工具的timeline.但是很遗憾,虽然大概懂了每一项是做什么的,但是用起来并不能得心应手.所以今天的重点不是timeline,而 ...

  9. Docker中images无法使用apt-get update解决方案

    问题描述:使用apt-get update一直fail 原因:DNS设置错误 解决办法: nm-tool #查看DNS后面的数(IPv4),将后面的数copy下来 sudo nano /etc/def ...

  10. Ambari组件黄色预警

    Ambari组件黄色预警 组件上为黄色问号,代表心跳丢失,解决如下: 1.  查看个节点之间是否可以相互通信,若ssh连接不上,有可能是该节点关机了,没有打开,手动开启该节点,再次验证是否可互通. 2 ...