ObjectBuilder技术内幕之五
创建器
从前面的论述中,我们看到一个对象的创建过程十分复杂和繁琐,远不是一个new那么简单,涉及到许多对象,创建器上下文、策略、方针等等等等。但由于采用了良好的设计模式,是这些众多的对象协同工作次序井然。创建器采用创建者设计模式,把一系列对象的创建工作加以封装,使调用者只要对其进行配置,然后调用BuildUp就可以得到最后的产品(要创建的对象),或者调用TearDown来销毁对象,BuildUp和TearDown是一对开闭操作,你使用BuildUp创建的对象,最好使用TearDown将其销毁。原因我想你也清楚,因为对象的创建如果涉及到其他对象(尤其是在依赖注入的情况下),使用TearDown尤其重要。TearDown会以对象被创建的相反次序卸载对象。下面是我们看创建器的代码的时候了,同样按照惯例从接口开始:
public interface IBuilder<TStageEnum>
{
PolicyList Policies { get; }
StrategyList<TStageEnum> Strategies { get; }
object BuildUp(IReadWriteLocator locator, Type typeToBuild, string idToBuild, object existing, params PolicyList[] transientPolicies);
TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator, string idToBuild, object existing, arams PolicyList[] transientPolicies);
TItem TearDown<TItem>(IReadWriteLocator locator, TItem item);
}
IBuilder接口就像我们期待的那样简单,两个重载的BuildUp版本,一个用于泛型。一个TearDown方法。两个属性:策略表和方针表。
可能你注意到有点和策略接口方法相同,一个区别就是创建器的BuildUp方法参数和策略中的参数不同。由于对象的创建过程需要一个创建器上下文,配置上下文的工作必须由创建器来完成。在IBuilder下是一个抽象的BuilderBase基类, 我们只讲解主要部分:
private Dictionary<object, object> lockObjects = new Dictionary<object, object>();
该字段成员用于支持多线程操作。
public BuilderBase(IBuilderConfigurator<TStageEnum> configurator)
{
configurator.ApplyConfiguration(this);
}
构造器之一,参数configurator是实现了IBuilderConfigurator接口的类实例对象,通过这种方式,创建器的配置工作可以被分离到外部,使得对创建器的使用更加灵活。比如说你可以从IBuilderConfigurator接口实现一个你自己的配置类,然后在ApplyConfiguration方法对传递给方法的Builder对象进行配置(访问者模式的应用)。
public virtual object BuildUp(IReadWriteLocator locator, Type typeToBuild,
string idToBuild, object existing, params PolicyList[] transientPolicies)
{
if (locator != null)
{
lock (GetLock(locator))
{
return DoBuildUp(locator, typeToBuild, idToBuild, existing, transientPolicies);
}
}
else
{
return DoBuildUp(locator, typeToBuild, idToBuild, existing, transientPolicies);
}
}
基类的默认BuildUp过程,如果定位器不为null,对定位器加锁(阻止其他线程在此期间对它进行访问)。调用DoBuildUp方法。DoBuildUp方法如下:
private object DoBuildUp(IReadWriteLocator locator, Type typeToBuild, string idToBuild,
object existing, PolicyList[] transientPolicies)
{
IBuilderStrategyChain chain = strategies.MakeStrategyChain();
ThrowIfNoStrategiesInChain(chain);
IBuilderContext context = MakeContext(chain, locator, transientPolicies);
IBuilderTracePolicy trace = context.Policies.Get<IBuilderTracePolicy>(null, null);
if (trace != null)
trace.Trace(Properties.Resources.BuildUpStarting, typeToBuild,
idToBuild ?? "(null)");
object result = chain.Head.BuildUp(context, typeToBuild, existing, idToBuild);
if (trace != null)
trace.Trace(Properties.Resources.BuildUpFinished, typeToBuild,
idToBuild ?? "(null)");
return result;
}
首先创建策略链,然后调用ThrowIfNoStrategiesInChain检测,如果没有策略(空链)则抛出异常。我们从这里可以看到派生类在调用基类的BuildUp之前,必须先把策略添加到策略表中。接下来是调用MakeContext方法构造创建器上下文环境。接着查询方针表看是否需要跟踪。最后通过调用策略链的第一个策略的BuildUp方法开始对象的创建过程。MakeContext方法如下:
private IBuilderContext MakeContext(IBuilderStrategyChain chain,
IReadWriteLocator locator, params PolicyList[] transientPolicies)
{
PolicyList policies = new PolicyList(this.policies);
foreach (PolicyList policyList in transientPolicies)
policies.AddPolicies(policyList);
return new BuilderContext(chain, locator, policies);
}
构造创建器上下文过程并不复杂,但是这里要注意到一个transierntPolicies参数,这是通过参数传递给BuildUp方法的称为瞬间方针表,也就是说它并未被永久的保存到Builder对象的方针表中,仅在本次的创建过程有效。这种机制允许我们在创建一个特殊对象时避免创建一个新的创建器。
public TItem TearDown<TItem>(IReadWriteLocator locator, TItem item)
{
if (typeof(TItem).IsValueType == false && item == null)
throw new ArgumentNullException("item");
if (locator != null)
{
lock (GetLock(locator))
{
return DoTearDown<TItem>(locator, item);
}
}
else
{
return DoTearDown<TItem>(locator, item);
}
}
对象的拆卸过程,如果要拆卸的对象不是值类型同时是null的话,抛出异常。如果定位器不为空,锁住定位器(理由同BuildUp),然后调用DoTearDown方法,开始拆卸工作。
private TItem DoTearDown<TItem>(IReadWriteLocator locator, TItem item)
{
IBuilderStrategyChain chain = strategies.MakeReverseStrategyChain();
ThrowIfNoStrategiesInChain(chain);
Type type = item.GetType();
IBuilderContext context = MakeContext(chain, locator);
IBuilderTracePolicy trace = context.Policies.Get<IBuilderTracePolicy>(null, null);
if (trace != null)
trace.Trace(Properties.Resources.TearDownStarting, type);
TItem result = (TItem)chain.Head.TearDown(context, item);
if (trace != null)
trace.Trace(Properties.Resources.TearDownFinished, type);
return result;
}
拆卸对象的过程和创建过程正好相反,首先用一条反向的策略链(和创建过程的策略次序相反),然后调用MakeContext方法创建一个上下文环境,最后调用策略链的第一个策略的TearDown方法开始对象的拆卸过程。
private object GetLock(object locator)
{
lock (lockObjects)
{
if (lockObjects.ContainsKey(locator))
return lockObjects[locator];
object newLock = new object();
lockObjects[locator] = newLock;
return newLock;
}
}
加锁机制,使用前面定义的字典对象作为哨兵,如果字典中已经有定位器存在,就返回这个定位器,否则把定位器加入到字典中。
在抽象基类的层次下面,是Builder的具体类,由于基类已经实现了基本的公共操作,剩下的操作由Builder类来完成。Builder类只有一个构造器方法,这个方法我们前面已经看到了。我们没有提到的是最后的两行代码:
Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
if (configurator != null)
configurator.ApplyConfiguration(this);
代码在设置了策略之后,设置一个指定一个默认的创建方针。最后一个语句作用和基类一样,就不解释了。
我们几乎讲述了OB的全部代码,还有一些例如自定义异常类等,相信你自己就能看明白。另外
CAB中带有OB的单元测试代码,如果你对某个类或者有些方法还不能完全理解的话,可以看相应的测试用例。
后记
OB的设计中,包含了大量的设计模式,比如整个创建器就是创建者设计模式,模式的实现过程包含了责任链设计模式、策略设计模式、组合设计模式、模板方法设计模式、黑板设计模式、访问者设计模式等,每一种设计模式都不是孤立的,如果你对设计模式的使用感到困惑,那么OB就是最好的教材。
本系列作为新年礼物送给大家,希望你们能够喜欢。文稿没有经过校对和润色,有许多错字别字,还望大家多多包涵,如果有什么批评和建议,也请不吝赐教。
(全文完)