简介
建造者模式(Builder Pattern):将一个复杂对象的构建和表示分离,使得同样的构造过程可以创建不同的表示。
- 对于某些复杂对象,用户无须知道具体的构造细节,只需要指定类型即可。这时可以将对象本身和组装过程分开,并且将内部构成和组装方式隐藏,提供用户调用。
- 建造者模式关注一步步创建复杂对象,返回一个完整的复杂产品。
抽象工厂模式返回一系列相关产品。
结构和实现
- 角色包括:
- 抽象建造者:创建对象的接口,包括创建对象各个部件、返回复杂对象。
- 具体建造者:创建对象的实现类(返回对象可由抽象建造者实现)。
- 产品:被创建的对象,包含多个组成部分。
- 指挥者:调用创建者,安排构造对象的次序,完成对象的创建,并与客户端交互。
- 结构。
- 客户端调用。
……
Builder builder = new ConcreteBuilder(); //可通过配置文件实现
Director director = new Director(builder);
Product product = director.construct();
……
实例
- 创建游戏角色这一复杂对象,包含类型、性别、脸型、服饰、发型等组成部分。不同类型的角色组成部分的特性有所区别,但是创建步骤大同小异。
- 游戏角色创建结构图。
- 客户端调用。
public class Client
{
public static void main(String args[])
{
ActorBuilder ab; //针对抽象建造者编程
ab = (ActorBuilder)XMLUtil.getBean(); //反射生成具体建造者对象
ActorController ac = new ActorController();
Actor actor;
actor = ac.construct(ab); //通过指挥者创建完整的建造者对象
}
}
省略指挥者
- 将指挥者和抽象建造者合并,在抽象建造者中提供construct方法,逐步构建产品。
- 省略指挥者的游戏角色创建结构图。
- 客户端调用。
……
ActorBuilder ab;
ab = (ActorBuilder)XMLUtil.getBean();
Actor actor;
actor = ab.construct();
……
引入钩子方法
- 使用钩子方法控制某个部件的创建步骤是否进行。可在抽象建造者中提供默认的钩子方法,在具体建造者中可覆盖该方法,以单独控制创建流程。
- 这样复杂产品的构建顺序可控,构建的组成可控。
- 引入钩子方法的游戏角色创建图。
优缺点和适用范围
- 优点:
- 对象的创建和使用分离。相同的创建过程可以创建不同对象。
- 符合开闭原则。指挥者类针对抽象建造者编程,可以方便的更换具体建造者,创建不同的对象,增加建造者也不修改源码。
- 精细控制创建过程。可以分解创建对象的步骤,并且控制整个流程。
- 缺点:
- 适用组成相似的产品。组成差异很大的产品不适用。
- 创建者类较多。特别是产品的内部变化复杂时,会极大提高系统复杂度,增加运行成本。
- 适用范围:
- 产品对象内部复杂,包含多个成员变量。
- 产品对象组成相互依赖,需要一定的顺序。
- 对象的创建过程独立于该对象的类。可以将创建过程封装在指挥者类中。
- 需要隔离对象的创建和使用,相同的创建过程创建不同的产品。
jdk中的应用
- javax.xml.parsers.DocumentBuilder中的parse方法解析输入,创建Document对象。
- 指挥者为DocumentBuilderImpl,抽象创建者为AbstractDOMParser,创建者为DOMParser,产品为Document。
- 指挥者为DocumentBuilderImpl,抽象创建者为AbstractDOMParser,创建者为DOMParser,产品为Document。
public Document parse(InputSource is) throws SAXException, IOException {
if (is == null) {
throw new IllegalArgumentException(
DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
"jaxp-null-input-source", null));
}
if (fSchemaValidator != null) {
if (fSchemaValidationManager != null) {
fSchemaValidationManager.reset();
fUnparsedEntityHandler.reset();
}
resetSchemaValidator();
}
domParser.parse(is);
Document doc = domParser.getDocument();
domParser.dropDocumentReferences();
return doc;
}