设计模式四-----创建者模式
设计模式四-----创建者模式
创建者模式它一般用于创建复杂对象,从独立创建每个部分到最后的组装,他要承担一系列工作。由于把他创建的每各部分都独立为一个单一的过程,因此不仅可以完后才能较为“精细”的创建,还可以编排创建步骤,生成不同的目标实例。
在单件模式和抽象工厂模式中,对经典设计模式进行扩展时,构造的职责与指导构造过程的职责分别被不同对象所承担,同样,创建者模式中的组装和执行次序编排也需要一个顾问来指导,也就是需要增加一个Driector对象。不仅如此,在实际项目中,有时还需要一个“拆除”的过程。也就是说,客户程序可能除了要调用一个BuilderUp()方法外,偶尔还需要调用TrearDown(),目的是在运行时让对象的装配动态化。
创建者模式的精髓部分—实现对复杂对象反复锻造的过程。
创建者模式适用的情景是产品局部加工过程变化较大,但组装过程相对固定。
经典创建者模式静态结构如下图,
角色:
Bulider:描述创建一个产品各个组成的抽象接口
Concrete Bulider:实现Bulider要求的内容,提供一个获得最终产品的方法
Director:指导产品加工的步骤,但指导完全基于Bulider的抽象方法
最终加工的产品类型并没有统一抽象为Product接口,主要原因是经过不同Concrete Bulider加工后的产品差别相对较大,给它一个公共的基准抽象对象意义不大,而且GetResult()方法仅仅位于具体Concrete Bulider类型中。
在实际项目中,我们之所以使用创建者模式往往有很明确的意图,即,主要用它来加工某一类产品,只不过系统通过不同的中间步骤“加工”出的产品在具体组成上有所不同。也就是说,实际项目中每个创建者加工的产品类型其实一般很明确,进而Builder接口和每个Concrete Bulider的各个BuliderPart()步骤一般也都是针对这个特定类型产品设计的。
一、创建者模式的优点
1、创建者模式将复杂对象的每个组成创建步骤暴露出来,借助Direct/客户程序既可以选择其执行次序,也可以选择要执行的步骤。上述过程可以在应用中动态完成,相比较工厂方法和抽象工厂模式的一次性创建过程而言,创建者模式适合创建“更为复杂且每个组成”的类型。
2、向客户程序屏蔽了对象创建过程的多变性,同样是经过一系列的创建过程,前面造汽车,后面造摩托。
3、构造过程的最终成果可以根据实际变化的情况,选择使用一个统一的接口,或者不同类的对象,给客户更大的灵活度。
二、创建者模式的缺点
相对而言,创建者模式会暴露更多的执行步骤,需要Director/客户程序具有更多的领域知识,如果使用不慎很容易造成更为紧密的耦合。因为Director很可能成为瓶颈,它无法稳定,最后影响相关类型也难以稳定。
三、为Builder打标签(C#)
Director起到说明书的作用,.NET平台有很好的的“贴标签”机制,就是用属性来扩展目标产品类型的创建信息,这样我们可以先完成一个非常通用的Builder类,由它自己或Director根据说明书对照每个零件上的标志信息装配各种产品。
其次,只要在类型定义的时候指定那些步骤将被Builder执行,同时告知其执行次序、执行次数即可。至于读“说明书”的工作就交给Director完成,也可以将这些逻辑内部转化为Builder Base的通用逻辑,这样客户程序使用时就很省心了。
对于标签具体的使用步骤(具体不做介绍,本人主修Java):
定义指导属性
工具类型设计
依据标签的装配过程
项目中标签使用(简述)
1、目标类型导向型:如果待构造的目标类型本身就很复杂,eg,它的每个成员本身又是一个复杂的对象,这时将属性直接贴在目标类型上,然后设计一个很通用的Director也许比较明智,从而达到以不变应万变。
2、创建者导向型:如果创建过程仅涉及某个Builder接口比较固定的几个方法,目标类型抽象后差异不大,这时可以考虑将属性贴在Builder/Concrete Builder上。
无论是哪种类型,采用标注方式在实现上相对复杂一些,下游开发人员使用却非常方便:
1.不必反复地编写多个不同的创建者,只要贴上标签即可
2.客户程序使用简介、一致
3.可以将标签和配置文件相结合,进一步提高在生产环境中标签指示内容的灵活性
四、 具有装配/卸载能力的Builder
当我们创建某个实例后,往往还要考虑如何让GC能够在合适的时候及时回收,这时除了product=null还需考虑为Builder增加TearDown()方法封装“拆卸”逻辑。eg,一个Database对象在被置为null前最好关闭已经打开的连接,如果引用了一个Socket端口,也最好把它释放掉。
为了实现一个具有动态能力的创建者,我们把拆卸功能要求TearDown()定义在IBuilder中。由于BuildUp和TearDown()是成对的操作,因此实际项目中如果BuildUp()比较复杂,建议依据备忘录模式,为Builder配置一个“备忘录”,便于指导TearDown()的拆卸过程。
五、连贯接口形成的Builder
上面我们定义的BuildUp()/TearDown()过程都是配合显示或者隐式Director完成的(不论是基于方法调用还是通过解析属性)。但有时装配过程的复杂性不在于类型每个组成多复杂,而是由于构造参数排列组合太多引起的,而且这些不是在编译过程就可以决定的,是允许客户程序“按需”动态定义的构造过程。
可以由内部Builder管理外部Entry的实例化过程,如还可以管理Entry实例的缓存,提高系统资源重用率(享元模式)。实际构造过程仅在Entry的private构造函数中一次完成,整个构造过程保持一致性。
但是编码量较多,在实际项目中,类似的处理可以用于哪些构造参数排列组合众多,但类型自身不容易变化的情况,例如复杂的配置对象、系统中一个全局的缓存管理对象等。对于软件行业,如果涉及的单证数据结构和复杂,但变化频率不大的时候,也可以考虑应用内置的Builder类实现构造过程。
六、小结
在创建型模式中,创建者模式往往被用来做一些“精细活”,它除了创建对象之外,还要负责把复杂的对象及其各部分组装起来。由于“组装工艺”的不同,构造出来的产品看呢过完全不一样。
项目中的扩展:
1.为了不用反复地实现IBuilder,我们给目标对象及它的某些方法“贴上标签”,借助反射和Attribute,实现具有动态机制的创建者。
2.构造出来的产品释放过程,释放后可能变得更加复杂的案例(BuildUp/TearDown的创建者)。
3.对于构造过程中构造参数排列组合过多的情况,我们将Builder引入对象内部,解决对象内部状态配置过程的复杂性。