一  需求分析

在LCD CIMS(Computer Integrated Manufacture System)中,有这样一个需求。系统中需要创建Equipment对象,这些对象由Machine对象和多个Port对象组成。Port对象包含两种类型:Input与Output,而Machine对象中,PortType应该与Port对象的类型相对应。从目前的需求来看,在Equipment中,存在如下三种构成情况:
(1)一个Machine对象,一个Input类型的Port对象;
(2)一个Machine对象,一个Output类型的Port对象;
(3)一个Machine对象,一个Input类型的Port对象,一个Output类型的Port对象;
客户方承认,不排除将来有增加新的Equipment组合的可能。
需求清晰而简单,我们可以非常容易地识别出Port、Machine、Equipment对象。Port类的定义如下:
    public abstract class Port
     {
        public abstract void Transfer();
     }

public class InputPort:Port
     {
        public override void Transfer()
        {
            Console.WriteLine("Input");
        }
     }
     public class OutputPort : Port
     {
        public override void Transfer()
        {
            Console.WriteLine("Output");
        }
     }
由于Port对象由于类型的不同,其方法Transfer()会有不同的实现,因此,定义了一个抽象Port类,然后定义其子类InputPort和OutputPort,分别代表两种不同类型的Port。
Machine类的定义相对比较简单:
     public class Machine
     {
        private string m_name;
        private string m_portType;

public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

public string PortType
        {
            get { return m_portType; }
            set { m_portType = value; }
        }

public Machine()
        {
        }

public Machine(string name)
        {
            m_name = name;
        }

public void Run()
        {
            Console.WriteLine("The machine {0} is running!",m_name);
        }
     }
由于Equipment对象可能会包含多个Port,因此在Equipment类的定义中,我引入对象用来保存Port对象,且利用泛型限制了集合元素的类型,从而通过强类型的方式避免强制转换时可能出现的异常或错误:
      public class Equipment
      {
        private Machine m_machine;
        private List<Port> m_list;
        private string m_name;

public Machine Machine
        {
            get { return m_machine; }
            set { m_machine = value; }
        }
        public List<Port> PortsList
        {
            get { return m_list; }
            set { m_list = value; }
        }       
        public string Name
        {
            get { return m_name; }
            set { m_name = value; }
        }

public Equipment()
        {           
            m_list = new List<Port>();
        }

public void AddPort(Port port)
        {
            m_list.Add(port);
        }
        public void Run()
        {
            Console.WriteLine("The Equipment {0} is running as below...",m_name);
            foreach (Port port in m_list)
            {
                port.Transfer();
            }
            m_machine.Run();
        }
      }
Equipment的Run()方法会遍历List<Port>中的元素,并执行Port类型的Transfer()方法,然后再执行Machine对象的Run()方法。

二、 坏的设计

单以现有的需求而论,上面的设计已经基本能够满足客户的要求。例如我们创建一个Equipment对象,该对象除包含一个Machine对象,以及两个Port对象,一个为Input类型,一个为Output类型:
Machine machine = new Machine("InputOutputMachine");
Port inputPort = new InputPort();
Port outputPort = new OutputPort();
Equipment eqp = new Equipment();
eqp.Name = machine.Name;
eqp.PortType = "InputOutput";
eqp.AddPort(inputPort);
eqp.AddPort(outputPort);
然而这样的实现存在以下三个明显的缺陷:
(1)创建过程过于繁杂,不便于调用方调用;
(2)Port对象的添加过程无法控制;
(3)过于僵化,不利于程序的扩展;
事实上,每当我们创建一个Equipment对象时,都需要执行与上面实现相似的代码,那么为何不将这样的创建职责进行封装,以便于程序的重用呢?既然是和对象的创建有关,我们首先想到的是工厂模式。既然是创建三种Equipment对象,利用简单工厂模式即可,例如定义一个类SimpleLCDFactory:
public static class SimpleLCDFactory
{
 public static Equipment CreateInputEQP(string eqpName)
 {
  //实现略;
 }
 public static Equipment CreateOutputEQP(string eqpName)
 {
  //实现略;
 }
 public static Equipment CreateIOPutEQP(string eqpName)
 {
  //实现略;
 }
}
因此我们要创建一个Equipment对象就相对容易多了:
Equipment eqp = SimpleLCDFactory.CreateIOPutEQP("InputOutputMachine");
由于提供了专门的方法来创建Equipment对象,之前提到的前两个缺陷就被有效地克服了。然而,由于采用的简单工厂模式,它并没有将可能存在的创建变化进行抽象,导致这样的结构仍然僵化,不易于扩展。例如,增加一个新的Equipment对象组合时,就需要修改SimpleLCDFactory类,增加一个新的方法,来创建这个对象。
要支持未来需求可能的变化,我们也可以引入Factory Method模式,将创建Equipment对象的方法进行抽象,具体的实现留待各自的实现子类来完成。坦白说,这未尝不是一个好的实现方案。然而,考虑到这里要创建的Equipment对象,是由Port对象和Machine对象组成的,这里我们关注的创建行为,归根结底,是如何将Port对象和Machine对象创建好,并使之组合成我们需要的整体对象Equipment,因此,采用Builder模式也许是更好的选择。
注意:实际上Factory Method模式与Builder模式并没有泾渭分明的区别,尤其针对Builder模式,我们都可以将其转换为Factory Method模式,反之则不然。也就是说,Factory Method模式的应用更加广泛。如果一定要作出区分,那么可以说,二者同时生成产品,Factory Method模式主要用于生成一类完整的产品,而Builder模式则更关注与产品内部各种零件的组合,并最终组装为整体的产品。由于Builder模式对产品内部的创建进行了细分,因此对于那些内部具有一定结构的目标对象,如本例的Equipment,最佳选择仍然是Builder模式。

三、 引入Builder模式

关于Builder模式而言,最重要的当然是Build行为的抽象。以本例而言,要创建一个Equipment对象,需要Build的是Port和Machine对象,所以我们可以完成两个Build方法的抽象:
void BuildPort();
void BuildMachine(string machineName);
按照这样一个原则,我们可以建立如图18-1所示的类图:
 
图18-1 Builder类的继承体系
实现代码如下:
    public abstract class EQPBuilder
     {       
        protected Equipment m_equipment;
        protected Machine m_machine;

public EQPBuilder()
        {
            m_equipment = new Equipment();
        }

public abstract void BuildPort();
        public virtual void BuildMachine(string name)
        {
            m_machine = new Machine(name);           
            m_equipment.Name = name;
            m_equipment.Machine = m_machine;
        }
        public virtual Equipment GetEQP()
        {
            return m_equipment;
        }
     }

public class InputEQPBuilder : EQPBuilder
     {       
        public override void BuildPort()
        {
            Port port = new InputPort();
            m_equipment.AddPort(port);
        }
        public override void BuildMachine(string name)
        {
            base.BuildMachine(name);
            m_machine.PortType = "Input";           
        }
     }

public class OutputEQPBuilder : EQPBuilder
     {       
        public override void BuildPort()
        {
            Port port = new OutputPort();
            m_equipment.AddPort(port);
        }
        public override void BuildMachine(string name)
        {
            base.BuildMachine(name);
            m_machine.PortType = "Output";           
        }
     }

public class IOPutEQPBuilder : EQPBuilder
     {       
        public override void BuildPort()
        {
            Port inputPort = new InputPort();
            m_equipment.AddPort(inputPort);
            Port outputPort = new OutputPort();
            m_equipment.AddPort(outputPort);
        }
        public override void BuildMachine(string name)
        {
            base.BuildMachine(name);
            m_machine.PortType = "InputOutput";           
        }
     }
在Builder子类中,由于Port对象的创建各不相同,因此在父类EQPBuilder中CreatePort()方法被定义为抽象方法,而创建Machine对象的CreateMachine()方法,则因为具有一些共同的逻辑,从而被定义为虚方法,并将相同的逻辑抽象到父类中,而子类中,仅根据创建的Port对象的不同,而分别设置Machine对象的PortType值。
此外,在抽象类EQPBuilder中,我还定义了一个虚方法GetEQP(),该方法用于返回一个Equipment对象,而这个对象实则就是EQPBuilder类型对象所创建的产品。由于各个Builder子类返回Equipment对象的实现逻辑完全一样,因此在子类中就不必改写该方法了。
此时,对于Builder模式而言,我们已经有了Product(产品)角色,即Equipment;有了Builder(建造者)角色,即EQPBuilder,以及它的派生子类。现在还缺乏一个Director(指导者)角色,用以引入具体建造者角色,指导完成产品的创建。该角色类似于工厂模式中的工厂对象。因此,我将其定义为LCDFactory,便于调用者理解其职能:
    public static class LCDFactory
     {
        public static Equipment CreateEQP(EQPBuilder buider,string name)
        {
            buider.BuildPort();
            buider.BuildMachine(name);
            return buider.GetEQP();
        }
      }
由于LCDFactory类的静态方法CreateEQP()接受的参数是抽象类EQPBuilder,因此指导者角色与建造者角色之间是弱依赖关系,这保证了Builder的扩展不会影响产品的创建,其类图如18-2所示:
 
图18-2 Builder模式的实现类图
与标准的Builder模式不同,为了调用方便,我将EQPBuilder类型中的GetEQP()方法也封装到了LCDFactory类中,因此客户端的调用方式应该是这样:
    class Program
     {
        static void Main(string[] args)
        {
            EQPBuilder builder = new InputEQPBuilder();
            Equipment eqp = LCDFactory.CreateEQP(builder,"InputMachine");
            eqp.Run();

builder = new IOPutEQPBuilder();
            eqp = LCDFactory.CreateEQP(builder,"InputOutputMachine");
            eqp.Run();

Console.Read();
        }
}

四、 从容应对扩展

即使创建Equipment的需求发生了变化,应用了Builder模式的设计也能够从容应对。例如要求创建Equipment包含一个Machine对象,一个Input类型的Port,两个Output类型的Port,那么我们可以在不修改原有程序集的前提下,新定义一个IO2PutEQPBuilder类,并继承自抽象类EQPBuilder:
    public class IO2PutEQPBuilder : EQPBuilder
     {       
        public override void BuildPort()
        {
            Port inputPort = new InputPort();
            m_equipment.AddPort(inputPort);
            Port outputPort1 = new OutputPort();
            m_equipment.AddPort(outputPort1);
Port outputPort2 = new OutputPort();
            m_equipment.AddPort(outputPort2);
        }
        public override void BuildMachine(string name)
        {
            base.BuildMachine(name);
            m_machine.PortType = "InputOutput2";           
        }
}
而对于客户端调用而言,则根据传递进来的EQPBuilder类型对象,从而生产出不同的Equipment产品,我们甚至可以引入配置文件,利用反射技术动态创建具体的EQPBuilder对象,从而完全达到松散耦合且易扩展的目的。一个模式的引入,给程序结构带来了有利的变化,如果能够合理地运用,就足以弥补好的设计与坏的设计之间巨大的鸿沟。

转载于:https://www.cnblogs.com/wayfarer/archive/2006/09/16/506292.html

Buider模式应用实践相关推荐

  1. Command模式的实践

    Command模式的实践 主题 文章将通过一个简单的 Shell  库来阐述 Command模式的一个实际应用. 如何使用 Shell 库 在开始之前,我们先来看看如何使用这个 Shell 库. 解决 ...

  2. 《敏捷软件开发:原则、模式与实践(C#版.修订版)》—第1章1.4节参考文献

    本节书摘来自异步社区<敏捷软件开发:原则.模式与实践(C#版.修订版)>一书中的第1章1.4节参考文献,作者[美]Robert C. Martin , Micah Martin,更多章节内 ...

  3. 微软模式与实践团队发布Enterprise Library 4.1及Unity Application Block 1.2

    微软模式与实践团队发布Enterprise Library 4.1及Unity Application Block 1.2,具体可参看InfoQ的新闻http://www.infoq.com/cn/n ...

  4. 敏捷软件开发:原则、模式与实践(C#版)

    刚才在china-pub看到<敏捷软件开发:原则.模式与实践(C#版)>已经出版了.这本书是以前那本<敏捷软件开发:原则.模式与实践>的C#版,这是不是说明C#程序员的数量已经 ...

  5. 敏捷软件开发:原则、模式与实践——第12章 ISP:接口隔离原则

    第12章 ISP:接口隔离原则 不应该强迫客户程序依赖并未使用的方法. 这个原则用来处理"胖"接口所存在的缺点.如果类的接口不是内敛的,就表示该类具有"胖"接口 ...

  6. 基于阿里云移动推送的移动应用推送模式最佳实践

    摘要: ### 一.概念 以下概念对应系统设计时的语义,对于如何合理使用移动推送有借鉴意义 #### 1.1 设备 安装并使用开发者移动应用的装置 #### 1.2 设备ID 阿里云移动推送为设备分配 ...

  7. 深入PHP面向对象、模式与实践读书笔记:面向对象设计和过程式编程

    注:本文内容来<<深入PHP面向对象.模式与实践>>中6.2节. 6.2 面向对象设计与过程式编程   面向对象设计和过程式编程有什么不同呢?可能有些人认为最大的不同在于面向对 ...

  8. 云原生持续交付的模式和实践

    RIO(大众汽车公司的一个品牌)首席架构师Christian Deger最近在伦敦Continuous Lifecycle大会上分享了一系列实现云原生持续交付的模式和实践.Deger的演讲内容涵盖了用 ...

  9. php面向对象之策略模式,php策略模式的学习--引自《深入php面向对象模式与实践》...

    #策略(Strategy)模式 #定义抽象类 Lesson abstract class Lesson{ private $duration; private $coststrategy;#定义属性 ...

最新文章

  1. 【Protocol Buffer】Protocol Buffer入门教程(五):repeated限定修饰符
  2. pycharm设置anaconda并运行helloworld
  3. UliPad 初体验----python 开发利器
  4. [10.5模拟] dis
  5. 为什么私有GIT服务器上无法查看上传的代码?
  6. android 拍照 图片剪切
  7. 空循环和无穷循环的区别
  8. VMware workstaion上传虚拟机到VMware EXSI 5.5
  9. 次时代Java编程(一):续 vertx-sync实践
  10. scala连接mongodb_MongoDB 的用户配置与基于Scala的使用
  11. openmeetings(开源视频会议系统)的详细安装步骤 (windows版)
  12. foobar 2000|foobar2000中文版32/64位下载 v1.3.16
  13. Quartus II三种方式实现D触发器
  14. 库存转换是什么意思_安全库存的设置:库存计划的看家本领
  15. 密码学-->buuctf49~60
  16. 嵌入式硬件-读懂原理图
  17. Andriod 实现一个微信聊天框(一)
  18. 拓嘉辰丰:怎样把买家秀做好促进转化
  19. 求求你!不要在网上乱拷贝代码了
  20. linux live cd下载地址,Finnix 120 发布下载,基于Debian的Linux Live CD

热门文章

  1. c语言中堆栈_C语言编程中的堆栈
  2. python生成器_Python生成器
  3. 微软系统工具套装(Windows Sysinternals Suite)
  4. Java软件开发中,常用的工具有哪些?
  5. 建造者模式 生成器模式 创建型 设计模式(五)
  6. 搭建fabric-sample/balance-transfer网络详解
  7. 为全面到来的数字化未来准备就绪 戴尔科技峰会赋能企业数字化发展新动力
  8. MongoDB实战系列之二:MongoDB的常用操作
  9. man statd(rpc.statd中文手册)
  10. GraphX中Pregel单源点最短路径(转)