生成器(Builder)模式

生成器(Builder)模式

隶属类别——对象创建型


1. 意图

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

2. 别名

3. 动机

这是源生的传统意义的Builder模式,之所以和其他地方看到的不同的原因是因为那是“revised GoF Builder pattern”即修订版的Builder模式,代码示例中将的也是修订版的Builder模式。

修订版Builder Patter的动机

一个RTF(Rich Text Format)文档交换格式的阅读器应能将RTF转换为各种正文格式。该阅读器可以将RTF文档转换成普通ASCⅡ文本或转换成一个能以交互方式编辑的正文窗口组件。但问题在于可能转换的数目是无限的。因此要能够很容易实现新的转换的增加,同时不改变RTF阅读器应该怎么办呢?

一个解决方法是用一个可以将RTF转换成另一种正文表示的TextConverter对象配置这个RTFReader类。当RTFReader对RTF文档进行语法分析时,它使用TextConverter去做转换。无论何时RTFReader识别一个RTF标记(或是普通正文或是一个RTF控制字),它都发送一个请求给TextConverter去转换这个标记。TextConverter对象负责进行数据转换以及用特定格式表示该标记,如下图所示。

在这里插入图片描述

TextConvert的子类对不同的转换和不同格式进行特殊处理。例如,一个ASCIIConverter只负责转换普通文本,而忽略其他转换请求。另一方面,一个TeXConverter将会实现对所有请求的操作,以便生成一个获取正文中所有风格信息的TEX表示。一个TextWidgetConverter将生成一个复杂的用户界面对象以便用户浏览和编辑正文。

每种转换器类将创建和装备一个复杂对象的机制隐含在抽象接口的后面。转换器独立于阅读器,阅读器负责对一个RTF文档进行语法分析。

Builder模式描述了所有这些关系。每一个转换器在该模式中被称为生成器(Builder),而阅读器则称为导向器(director)。在上面的例子中,Builder模式将分析文本格式的算法(即RTF文档的语法分析程序)与描述怎么创建和表示一个转换后格式的算法分离开来。这使我们可以重用RTFReader的语法分析算法,根据RTF文档创建不同的正文表示——仅需使用不同的TextConverter的子类配置该RTFReader即可。

4. 适用性

在以下情况使用Builder模式

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构建的对象有不同的表示时。

5. 结构

在这里插入图片描述

6. 参与者

  • Builder(TextConverter)
    • 为创建一个Product对象的各个部件指定抽象接口。
  • ConcreteBuilder(ACSIIConverter、TexConverter、TextWidgetConverter)
    • 实现Builder的接口以构造和装配该产品的各个部件
    • 定义并明确它所创建的表示
    • 提供一个检索产品的接口(例如:getASCIIText和getTextWidget)
  • Director (RTFReader)
    • 构造一个使用Builder接口的对象
  • Product (ASCIIText, TexText, TextWidget)
    • 表示被构造的复杂对象。ConcreteBuilder创建该产品的内部表示并定义它的装配过程。
    • 包括定义组成部件的类,包括将这些部件装配成最终产品的接口

7. 协作

  • 客户创建Director对象,并用它想要的Builder对象进行配置。
  • 一旦产品部件被生成,导向器就会通知生成器。
  • 生成器处理导向器的请求,并将部件添加到该产品中。
  • 客户从生成器中检索产品。

下面的交互图说明了Builder和Director是如何与一个客户协作。

在这里插入图片描述

8. 效果

这里是Builder模式的主要优点:

  • 1)它使你可以改变一个产品的内部表示 Builder对象提供了导向器一个抽象的接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品是如何装配的。因为产品是通过抽象接口构造的,你在改变时所要做的只是定义一个新的生成器。

  • 2)它将构造代码和表示代码分开 Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。客户不需要知道定义产品内部结构的类的所有信息;这些类是不出现在Builder接口中的。每个ConcreteBuilder包含了创建和装配一个特定产品的所有代码。这些代码只需要写一次。然后不同的Director可以复用它们在相同部件集合的基础上构作不同的Product。在前面的RTF例子中,我们可以为RTF格式以外的格式定义一个阅读器,比如一个SGMMLReader,并使用相同的TextConverter生成SGML文档的ASCIIText, TextText和TextWidget译本

  • 3)它使你可对构造过程进行更精密的控制 Builder模式与以下就生成产品的创建型模式不同,它使在导向这的控制下一步下一步构造产品的。仅当该产品完成时,导向者才从生成器中取回它。因此Builder接口相比其它创建型模式更好地反映产品的构造过程。这使得你可以更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。

  • 4)Builder模式有良好灵活性 它可以有多个可变(varargs)参数。因为builder是利用单独的方法来设置每一个参数。构造器还将多次调用某一个方法而传入的参数集中到一个域中,如下调用两次addTopping方法的代码所示。

    NyPizza pizza = new NyPizza.Builder(SMALL).addTopping(SAUAGE)
        									 .addTopping(ONION).build();
    CalZONE calzone = new Calzone.Builder().addTopping(HAM)
        								   .sauceInside().build();
    

    Builder模式十分灵活,可以利用单个builder对象构建多个对象。builder的参数可以在调用build方法以达到创建对象期间进行调整的目的,builder的参数也可以随着不同的对象而改变你。builder可以自动填充某些域,例如每次创建对象时自动添加序列号。

缺点:

  • 1) 额外的创建开销 Builder模式也有自身的不足,为了创建对象,必须先创建它的Builder。虽然创建Builder的开销在实践中可能不那么明显,但是在某些十分注重性能的情况下,可能就成问题了。
  • 2)更加冗长 Builder模式还比重叠构造器冗长,因此它旨在有很多参数的时候才用,比如4个参数或者更多个参数。但是请记住,你未来可能需要添加参数。如果一开始就使用构造器在其或者静态工厂,等到类进化到参数数量失控的程度才切换到Builder模式,那么过时的构造器或静态工厂就像拇指一样伸出来,因此,通常最好一开始就使用Builder.

简而言之,如果类的构造器或者静态工厂中具体多个参数,设计这种类是,Builder模式就是一种不错的选择,特别当大多数参数都是可选或者类型相同的时候,与使用重叠构造器(重载构造器的方法)相比,使用Builder模式的客户端代码将更易于阅读和编写,Builder也比如JavaBeans安全许多。

9. 实现

通常有一个抽象的Builder类型为导向者可能要求创建的每一个构建定义一个操作。这些操作缺省情况下什么都不做。一个ConcreteBuilder类为它有兴趣创建的构件重定义这些操作。

这里是其他一些要考虑的实现问题:

    1. 装配和构造接口 生成器逐步构造它们的产品。因此Builder类接口必须足够普遍,以便为各种类型的具体生成器构造产品。

    一个关键的设计问题在于构造和装配过程的模型,构造的请求结果只是被添加到产品中,通常中这样的模型就以及足够了。在RTF的例子中,生成器转换下一个标记并将它添加到已经转换了的正文中。

    但有时你可能需要访问前面已经构造了的产品部件。

    1. 为什么产品没有抽象类 通常情况下,由具体生成器生成的产品,它们的表示相差是如此之大以至于给不同的产品以公共父类没有太大好处。 在RTF例子中,ASCII Text和TextWidget对象不可能有公共接口,它们也不需要这样的接口因为客户通常用合适的具体生成器来配置导向者,客户处于的位置使它知道Builder的哪一具体子类被使用和能相应的处理它的产品。
    1. 在Builder中缺省的方法为空

10. 代码示例

在Java一般通过内部嵌套类来实现Builder模式,其具体情况如下:

首先是Director——Hero.java

public class Hero {
   
	// required parameters
	private final Profession profession;
	private final String name;
	
	// optional parameters
	private Weapon weapon;
	private Armor armor;
	private HairStyle hairStyle;
	private HairColor hairColor;
	
	private Hero(HeroBuilder heroBuilder) {
   
		profession = heroBuilder.profession;
		name = heroBuilder.name;<
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值