建造者模式,将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
一个产品常常会有不同的组成成分作为零部件,这些零件可能是对象,也可能不是对象,他们通常由叫做产品的内部表象。不同的产品可以有不同的内部表象,也就是不同的零部件。使用建造者模式可以使客户端不需要知道所生成的产品对象有哪些零部件,每个产品的相应零部件有什么不同,是怎么建造出来的,以及是怎样组成产品的。建造者模式的简图如下
图 1. 建造者模式结构图
在这个示意图中,最终产品是由两部分组成的,所以相应的建造方法也有两个。这个模式涉及到一下四个角色。
抽象建造者角色:给出一个接口,规范产品对象的各个组成部分的建造。一般而言此接口独立于应用程序的商业逻辑。模式中直接创建产品的具体建造者,必须实现这个接口所要求的两种方法,一种是建造方法,比如图中BuildPart1()和BuildPart2;另外一种是结果返回方法,即图中的GetProduct()。一般来说产品所包含的零部件数目与建造方法的数目相符。
具体建造者角色:担任这个角色的是与应用程序紧密相关的一些类,他们在应用程序调用下创建产品的实例。其需要完成任务包括:
实现抽象建造者builder所声明的接口,给出一步一步创建产品实例的操作。
建造过程完成后,提供产品的实例。
导演者角色:其调用具体的建造者角色以创建产品对象。应当指出的是,导演者角色并没有产品类的具体知识,真正具有与产品类具体知识的是具体的建造者角色。
产品角色:产品就是模式中要建造的复杂对象。
导演者角色是与客户端打交道的角色。导演者角色将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但是却不为客户端所知。
一般而言,每有一个产品类,就有一个相应的具体建造者类。这些产品应当有一定数目的零件,而每有一个两件就相应的在所有建造者橘色里有一个建造方法。
JBPM中的建造者模式
对于这个模式,虽然JBPM中提供了几个亿builder结尾的类,但是这些类并没有在代码中使用,同时也不是很适合建造者模式应用的场景。最终选择的JBPM中获取缓存对象部分的代码实现,虽然从代码的意图符合建造者模式,但是并不完美还是有一些差距的。如果您觉得看不懂或者不正确,可以参考其他设计模式给出的实例代码。
图 2. JBPM中的建造者模式
我们知道JBPM自己实现了一套IOC,这套IOC为延迟加载全局对象、缓存全局对象、获取全局对象提供了便捷的方式。下面我们以Hibernate中Configuration为例,来说明这个模式的实现。
导演者角色:Context、Environment、ProcessEngine三者都提供了获取产品的接口,我们可以通过产品对象的类型或者名称来获取。
public interface Context {
Object get(String key);
<T> T get(Class<T> type);
}
public interface Environment {
/**
* searches a named object in all the contexts in the default search order.
* @return the object if it exists in the environment, <code>null</code> if there is no object with the given name in the environment.
*/
public abstract Object get(String name);
/** searches an object based on type. The search doesn take superclasses of the context elements
* into account.
* @return the first object of the given type or null in case no such element was found.
*/
public abstract <T> T get(Class<T> type);
}
public interface ProcessEngine {
/** retrieve and object defined in the process engine by type */
<T> T get(Class<T> type);
/** retrieve and object defined in the process engine by name */
Object get(String name);
}
抽象建造者角色:Descriptor和AbstractDescriptor作为抽象建造者,分别提供了构造产品对象和相应成员的方法construct和initialize。
public interface Descriptor extends Observable, Serializable {
/**
* constructs the object.
* @param wireContext {@link WireContext} in which the object is created. This is also the {@link WireContext}
* where the object will search for other object that may be needed during the initialization phase.
* @return the constructed object.
*/
Object construct(WireContext wireContext);
/**
* called by the WireContext to initialize the specified object.
* For more information about initialization, see {@link WireContext} section lifecycle.
* @param object object to initialize.
* @param wireContext the context in which the object will be initialized.
*/
void initialize(Object object, WireContext wireContext);
}
public abstract class AbstractDescriptor extends DefaultObservable implements Serializable, Descriptor {
private static final long serialVersionUID = 1L;
protected long dbid;
protected int dbversion;
protected int version;
protected String name = null;
/** lazy creation and delayed initialization */
public static final char INIT_LAZY='L';
/** eager creation and delayed initialization */
public static final char INIT_EAGER='E';
/** lazy creation and immediate initialization */
public static final char INIT_REQUIRED='R';
/** eager creation and immediate initialization */
public static final char INIT_IMMEDIATE='I';
protected char init = INIT_LAZY;
public AbstractDescriptor() {
}
public AbstractDescriptor(String name) {
this.name = name;
}
public Class<?> getType(WireDefinition wireDefinition) {
return null;
}
public boolean isEagerInit() {
return (init == INIT_EAGER || init == INIT_IMMEDIATE);
}
public boolean isDelayable() {
return (init == INIT_EAGER || init == INIT_LAZY);
}
public void initialize(Object object, WireContext wireContext) {
}
/** the db primary key. */
public Long getDbid() {
return dbid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/** see section 'Initialization' of {@link WireContext} */
public void setInit(char init) {
this.init = init;
}
}
具体建造者角色:具体的建造者角色就是建造对象的Descriptor类,这个例子中就是HibernateConfigurationDescriptor。
public class HibernateConfigurationDescriptor extends AbstractDescriptor {
private static final long serialVersionUID = 1L;
private static final Log log = Log.getLog(HibernateConfigurationDescriptor.class.getName());
String className;
String namingStrategyClassName;
List<Operation> cfgOperations = new ArrayList<Operation>();
List<Operation> cfgCacheOperations = new ArrayList<Operation>();
PropertiesDescriptor propertiesDescriptor = null;
public Object construct(WireContext wireContext) {
// instantiation of the configuration
Configuration configuration = null;
if (className!=null) {
try {
log.trace("instantiating hibernate configuration class "+className);
Class<?> configurationClass = ReflectUtil.classForName(className);
configuration = (Configuration) ReflectUtil.newInstance(configurationClass);
} catch (Exception e) {
throw new JbpmException("couldn't instantiate hibernate configuration class "+className, e);
}
} else {
log.trace("instantiating default hibernate configuration");
configuration = new Configuration();
}
return configuration;
}
public void initialize(Object object, WireContext wireContext) {
Configuration configuration = (Configuration) object;
apply(cfgOperations, configuration, wireContext);
apply(cfgCacheOperations, configuration, wireContext);
if (propertiesDescriptor!=null) {
Properties properties = (Properties) wireContext.create(propertiesDescriptor, false);
if (log.isDebugEnabled()) log.debug("adding properties to hibernate configuration: "+properties);
configuration.addProperties(properties);
}
}
private void apply(List<Operation> operations, Configuration configuration, WireContext wireContext) {
if (operations!=null) {
for (Operation operation: operations) {
log.trace(operation.toString());
operation.apply(configuration, wireContext);
}
}
}
public Class<?> getType(WireDefinition wireDefinition) {
if (className!=null) {
try {
return ReflectUtil.classForName(className);
} catch (Exception e) {
throw new WireException("couldn't create hibernate configuration '"+className+"': "+e.getMessage(), e.getCause());
}
}
return Configuration.class;
}
}
建造者模式的特点
1. 分离对象表示和对象的创建过程。由于建造者隐藏了产品具体组建过程,所以如果需要改变产品的内部表示,只需要中心定义一个具体的建造者就可以了。
2. 需要生成的产品对象的属性互相依赖。建造者模式可以强制实行一种分步骤的建造过程。因此,可以强制产品的一个属性必须在另一个属性之前赋值。
3. 在对象的创建过程中需要使用到系统中的其他一些对象,但是其在产品的创建过程中不容易得到。