产品的内部表象
一个产品常有不同的组成成分作为产品的零件,这些零件可能是对象,也可能不是对象,它们通常叫做产品的内部表象。不同的产品可以有不同的内部表象,也就是不同的零件,使用建筑模式可以使客户端不用知道所生成的产品有哪写零件,每个产品的对应零件彼此有什么不同,是怎么创建出来的,以及怎么组成产品。
对象性质的构造
有些情况下,一个对象会有一些重要的属性,在它们没有恰当的值之前,对象不能作为一个完整的产品使用。比如一封电子邮件,有发件人地址、收件人地址、主题、内容、附件等部分,在收件人地址得到赋值之前,这封邮件不能发出。
有些情况下,一个对象的属性必须按照某个顺序赋值才有意义,在某个属性没有赋值之前,另一个属性则无法赋值,这些情况使得属性本事的建造涉及到复杂的商业逻辑。
这时候,此对象相当于一个有待建造的产品,而对象的这些属性相当于产品的零件,建造产品的过程是建造零件的过程。由于建造零件的过程很复杂,因此这些零件的建造过程往往被 “ 外部化”到另一个称作建造者的对象里,建造者对象返还给客户端的是一个全部零件都构造完毕的产品对象。
建造模式利用一个导演者对象和具体建造者对象一个一个地建造出所有零件,从而建造出完整的产品对象。建造模式将产品的结构、产品的零件和建造过程对客户端隐藏起来,把对建造过程进行指挥的责任和具体零件建造者的责任分隔开,达到责任划分和封装的目的。
建造模式的结构
类图与角色
建造模式的类图如下:

在上图的系统中,最终产品Product只有两个零件,即part1和part2,相应的构造方法也有两个:buildPart1()和buildPart2()。同时可以看出本模式涉及到四个角色,它们分别是:
- 抽象建造者(Builder)角色:给出一个抽象接口,一规范产品对象的各个组成成分的建造。一般而言,这个接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者角色,具体建造者类必须实现这个接口所要求的两种方法:一种是建造方法,比如例子中的buildPart1()和buildPart2();另一种是结构返还方法,即例子中的retrieveResut()。一般来说,产品所包含的零件数目与建造方法的数目相等,即有多少零件,就有多少建造方法。
- 具体建造者(Concrete Builder)角色:担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序的调用下创建产品的实例,这个角色要完成的任务包括:1实现抽象创建者Builder所声明的接口,给出一步一步地完成创建产品实例的操作。2在建造过程完成后,提供产品的市里了。
- 导演者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象,应当指出的是,导演角色并没有产品类的具体属性和方法,真正拥有的是具体建造者角色。
- 产品(Product)角色:产品(product)便是建造中的复杂对象,一般来说,一个系统中,可能会有多于一个的产品类,而且这些产品类不一定有共同的接口,但是完全可以不相关联。
一般来说,每有一个产品类,就有一个相应的具体建造者类,这些产品应当有一样数目的零件,而每有一个零件就相应地在所有的建造者角色里有一个建造方法。
源代码
下图给出上图最简单的实现的示意性源代码:
1.产品类 Product.java
package com.model.builder.builder;
/**
* 产品类
* */
public class Product {
/**
* 与应用系统相关 代码
**/
}
2.抽象建造者类 Builder.java
package com.model.builder.builder;
/**
* 抽象建造者类
* */
public abstract class Builder {
/**
* 产品零件构造方法 1
**/
public abstract void buildPart1();
/**
* 产品零件构造方法 2
**/
public abstract void buildPart2();
/**
* 产品返还方法
* */
public abstract Product retrieveResult();
}
3.具体创建者类 ConcreteBuilder.java
package com.model.builder.builder;
public class ConcreteBuilder extends Builder{
private Product product = new Product();
@Override
public void buildPart1() {
// TODO Auto-generated method stub
}
@Override
public void buildPart2() {
// TODO Auto-generated method stub
}
@Override
public Product retrieveResult() {
// TODO Auto-generated method stub
return product;
}
}
4.导演者类 Director.java
package com.model.builder.builder;
/**
* 导演者角色类
* */
public class Director {
private Builder builder;
/**
* 产品构造方法
**/
public void construct(){
builder = new ConcreteBuilder();
builder.buildPart1();
builder.buildPart2();
builder.retrieveResult();
}
}
多个产品类
在上面的系统中只有一个产品类,对应的也只有一个具体建造者类,如果有两个产品类,就应该有两个具体建造者类,以此类推。下图是有两个产品类和具体建造者类的建造模式:

产品类Product1和Product2有各自的零件,它们有不同的内部表象。在这里建造模式提供了一个队建造过程的封装。使得客户端不需要知道产品类的建造细节,便可以使用一个建造接口生成具有不同的的内部表象的 Product1和Product2对象。
标示接口
在上面的类图中,应该注意的是,retrieveResult()方法是抽象建造者角色提供的,如果它返还的是Product1类型或者Product2类型的话,那么ConcreteBuilder1或者ConcreteBuilder2就会有问题,因为他们应当返还Product1类型或者Product2类型。
解决的方案是为两个具体产品类提供一个共同的接口,形成他们共同的类型。然而建造模式的产品类往往是没有太多关系的一些类,它们不大可能有共同的接口,因此使用一个标示接口为所有的具体产品类提供一个共同的类型就成为解决问题的方案,即所有的retrieveResult()都返回Product类型就可以了。
如果产品是第三方提供,不能修改,那么只好将retrieveResult()方法从抽象建造者中去掉,这样具体建造者角色就可以分别具有返回类型不同的retrieveResult()方法,如果导演者角色需要调用这个方法,而变量的明显类型是Builder,就必须先将变量类型进行向下类型转换,使之成为具体建造者类型,然后调用相应的retrieveResult()方法。必须指出的是这种没有抽象产品类或者公共产品接口的情况更为普通。
建造模式的活动序列
类图只能反映出模式的静态结构,对建造模式而言,同意重要的还有系统的活动图。
客户端负责创建导演者和具体建造者对象。然后客户把具体建造者对象交个导演者。客户一声令下,导演者具体操纵具体建造者,开始创建产品。具体建造者接到导演者的一个指令,便让照指令向产品上增加一个构件,当产品完成后,建造者把产品返还给客户端。
虽然客户端确实是具体建造者对象,但是操作具体建造者的任务却属于导演者对象。把创建具体建造者对象的任务交给客户端而不是导演者对象,是为了将导演者对象与具体建造者对象的耦合变成动态的,从而使导演者对象可以操纵数个具体建造者对象中的任何一个。
建造模式的实现
当一个系统有多个产品类的时候,怎么能够保证它们所对应的具体建造者类都有同样的接口?
当然,产品的内部结构细节大多可以与具体建造者类的接口无关,因为接口仅仅给出建造方法的特征而已。但是接口确实规定了具体建造者类有几个零件建造方法,也就是说,抽象建造者角色确实规定了产品类必须有同意数目的零件,以及具体有几个零件。如果有一些产品有较多的零件,而有些产品的零件数目较少,还能使用建造模式吗?
回答是肯定的,这些产品并不一定要有同意数目的零件才可以使用建造模式,如果一个产品有较少的零件,可以使用空的零件建造方法,忽略没有的零件。建造模式在实现时可以根据具体的情况做一些变化。
在什么情况下使用建造模式
在以下 情况下应当使用建造模式:
(1)需要生成的产品对象有复杂的内部结构,每一个内部成分本身可以是对象, 也可以仅仅是一个对象(即产品对象)的一个组成成分。
(2)需要生成的产品对象的属性相互依赖,建造模式可以强制实行一种分步骤进行的建造过程,因此,如果产品对象的一个属性必须在另一个属性被赋值之后才可以赋值,使用建造模式就是一个很好的设计思想。有时候产品对象的属性并没有依赖关系,但是在产品的属性没有设置之前,产品对象不能使用,这时产品对象的实例化、属性的赋值和使用仍然是分步骤进行的,因此建造模式仍然是有必要的。
(3)在对象创建过程中会使用到系统中的其他一些对象,这些对象在产品对象的创建过程中不易得到。
使用建造模式主要有以下效果:
- 建造模式的使用使得产品的内部表象可以独立的变化,客户端不必知道产品内部组成的细节。
- 每一个builder都是相对独立,与其他的builder无关。
- 模式所建造的最终产品更易于控制。
建造模式与抽象工厂的区别
建造模式与抽象工厂模式非常像,又都是用来创建同事属于几个产品族的的对象的模式。
在抽象工厂中,每一次工厂对象被调用时,都会创建一个完整的产品对象。建造模式则不同,它一点一点地建造出一个复杂的产品,而这个产品的组装过程就发生在建造者角色内部。建造者模式的客户端拿到的是一个完整的最后产品。抽象工厂模式处在更加具体的尺度上,建造模式则处在更加宏观的角度上。