策略和方针
有了定位器,就要涉及到对象的创建问题了。在BO中对象的创建时根据所定义的对象策略并划分不同的阶段来进行。对象的阶段定义使用一个枚举类型,定义如下:
public enum BuilderStage
{
//预创建阶段
PreCreation,
//创建阶段
Creation,
//对象初始化阶段
Initialization,
//初始化完成阶段
PostInitialization
}
四个阶段层次分明。每一个阶段都有不同的策略,每个策略都有对应的方针,这些策略使用责任链设计模式,形成一条流水线,一个对象的创建过程就像是一个产品经过一条精心设计的加工流水线一样。策略类的接口定义如下:
public interface IBuilderStrategy
{
//在责任链中对要创建的对象进行创建工作
object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild);
//在责任链中卸掉一个已创建的对象
object TearDown(IBuilderContext context, object item);
}
从代码中看,OB并不仅仅要负责对象的创建,也要负责对象的销毁工作。因为采用责任链模式,所以需要设计一个上下文对象来保存和传递必要信息。这个上下文就是实现了IBuilderContext接口的类。我们先来看一下IBuidlerContext接口的定义:
public interface IBuilderContext
{
//指向责任链的链头
IBuilderStrategy HeadOfChain { get; }
//定位器
IReadWriteLocator Locator { get; }
//提供给策略使用的方针表
PolicyList Policies { get; }
//当前策略的下一个建造策略的引用
IBuilderStrategy GetNextInChain(IBuilderStrategy currentStrategy);
}
接口中有一个陌生的成员就是方针表。在熟悉它之前,我们先看看什么方针的接口定义:
public interface IBuilderPolicy
{
}
哈,很失望是不是,接口中什么都没有!这是一个标识性的接口,它没有定义方法。因为它是提供给策略使用的,所以让策略来决定一个方针需要什么方法。
为了实现不同的创建策略,策略类的家族定义了一个公共的基类:
public abstract class BuilderStrategy : IBuilderStrategy
{
public TItem BuildUp<TItem>(IBuilderContext context, TItem existing, string idToBuild)
{
return (TItem)BuildUp(context, typeof(TItem), existing, idToBuild);
}
public virtual object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
IBuilderStrategy next = context.GetNextInChain(this);
if (next != null)
return next.BuildUp(context, typeToBuild, existing, idToBuild);
else
return existing;
}
public virtual object TearDown(IBuilderContext context, object item)
{
IBuilderStrategy next = context.GetNextInChain(this);
if (next != null)
return next.TearDown(context, item);
else
return item;
}
protected string ParametersToTypeList(params object[] parameters)
{
List<string> types = new List<string>();
foreach (object parameter in parameters)
types.Add(parameter.GetType().Name);
return string.Join(", ", types.ToArray());
}
protected void TraceBuildUp(IBuilderContext context, Type typeToBuild, string idToBuild, string format, params object[] args)
{
IBuilderTracePolicy policy = context.Policies.Get<IBuilderTracePolicy>(null, null);
if (policy != null)
{
string message = string.Format(CultureInfo.CurrentCulture, format, args);
policy.Trace(Properties.Resources.BuilderStrategyTraceBuildUp, GetType().Name, typeToBuild.Name, idToBuild ?? "(null)", message);
}
}
protected void TraceTearDown(IBuilderContext context, object item, string format, params object[] args)
{
IBuilderTracePolicy policy = context.Policies.Get<IBuilderTracePolicy>(null, null);
if (policy != null)
{
string message = string.Format(CultureInfo.CurrentCulture, format, args);
policy.Trace(Properties.Resources.BuilderStrategyTraceTearDown, GetType().Name, item.GetType().Name, message);
}
}
protected bool TraceEnabled(IBuilderContext context)
{
return context.Policies.Get<IBuilderTracePolicy>(null, null) != null;
}
}
这个基类实现了最基本的接口方法BuildUp和TearDown,两个代码过程是相似的,也就是从上下文获取下一个策略(如果有的话),然后下一个策略的相同方法。最后把对象返回。其他的几个方法是辅助方法,便于Debug中跟踪信息,跟踪信息则是采用实现了IBuidlerTracePolicy接口的类来进行。最后一个方法用来检测一个方针表中是否存在一个跟踪方针,如果没有,则表示不跟踪信息。从这几个辅助方法,我们也看到了策略是如何使用方针的,这是一个基类,它通过调用跟踪方针的Trace方法来对要创建的类进行跟踪(如果存在跟踪方针的化)。
一个责任链设计模式,应该存在一条链,在OB中这条链就是策略链,策略链的设计由一个接口定义和一个具体实现类组成,它们实际上是一个链表结构,实现的方法也是链表的操作方法(代码略)。但是,注意到一个事实,那就是策略链没有对链表中策略进行删除的方法,这是否表示一个策略一旦添加到创建过程中就不能被删除了呢?非也,其实策略链仅仅扮演定位策略在链中的次序,真正的策略是保存在策略表中。
策略表(StrategyList)
StrategyList类按照不同的创建阶段对策略进行分组储存,每一个阶段一个节点,每个节点存储这个相同阶段的策略,这种实现通过一个内部的字典对象来进行:
public class StrategyList<TStageEnum>
{
...
private Dictionary<TStageEnum, List<IBuilderStrategy>> stages;
...
}
策略表的 功能 是把策略添加到对应的阶段中、清除指定阶段的策略、清除全部策略。除此之外策略表还提供转配策略链表的工作,方法 MakeStrategyChain()用于装配正向链表,方法MakeReverseStrategyChain()用于装配反向链表。两个方法中的代码都很容易理解。类中每个方法中的Lock()语句是为了支持多线程。
方针表(PolicyList)
方针表也是采用字典结构来保存方针,和策略表不同的是,方针表并不区分创建阶段。方针表中保存的每一个方针对象在整个创建期间可以被任何策略所访问。不同的策略从方针表中读写相应的数据,大部分的数据也是通过方针表中的方针来传递。每一个保存在方针表中的方针通过一个称为方针键的对象作为它们在字典中的索引,这个键被设计为结构类型:
public struct BuilderPolicyKey
{
public BuilderPolicyKey(Type policyType, Type typePolicyAppliesTo, string idPolicyAppliesTo)
{
PolicyType = policyType;
BuildType = typePolicyAppliesTo;
BuildID = idPolicyAppliesTo;
}
private Type PolicyType;
private Type BuildType;
private string BuildID;
}
这个结构的字段都是私有的,想想为什么?注意到结构类型是值类型这样一个事实,那么具有相同字段值的两个结构值也是相同,所以把这样的结构作为键就很合适。
方针表中几个重载的Set和Get方法的代码不难看懂,所以就不作详细解释了。一个有趣的问题大家可以来思考,那就是会重复保存相同的方针对象吗?需要说明的是方针表中可以设置一个缺省的方针,方针表提供了设置和清除缺省方针的方法,缺省的方针表示该方针可以被应用到任何一个要创建的对象上面。
创建器上下文(BuilderContext)
前面我们介绍了创建器上下文的接口定义,也提到了创建器上下文是用来保存创建过程中的信息的,所以我们以这个创建器上下文的具体类作为本章的结束:
public class BuilderContext : IBuilderContext
{
private IBuilderStrategyChain chain;
private IReadWriteLocator locator;
private PolicyList policies;
protected BuilderContext()
{
}
public BuilderContext(IBuilderStrategyChain chain, IReadWriteLocator locator, PolicyList policies)
{
this.chain = chain;
this.locator = locator;
this.policies = new PolicyList(policies);
}
public IBuilderStrategy HeadOfChain
{
get { return chain.Head; }
}
public IReadWriteLocator Locator
{
get { return locator; }
}
protected void SetLocator(IReadWriteLocator locator)
{
this.locator = locator;
}
public PolicyList Policies
{
get { return policies; }
}
protected void SetPolicies(PolicyList policies)
{
this.policies = policies;
}
protected IBuilderStrategyChain StrategyChain
{
get { return chain; }
set { chain = value; }
}
public IBuilderStrategy GetNextInChain(IBuilderStrategy currentStrategy)
{
return chain.GetNext(currentStrategy);
}
}
BuilderContext类作为创建过程的信息载体,它包含了一个策略链表,一个策略使用的方针表以及一个用来保存创建对象的定位器。方法和代码不需要更多的解释,相信大家都能看懂。