ObjectBuilder技术内幕(五)

    技术2022-05-11  127

    ObjectBuilder技术内幕之五

    创建器

    从前面的论述中,我们看到一个对象的创建过程十分复杂和繁琐,远不是一个new那么简单,涉及到许多对象,创建器上下文、策略、方针等等等等。但由于采用了良好的设计模式,是这些众多的对象协同工作次序井然。创建器采用创建者设计模式,把一系列对象的创建工作加以封装,使调用者只要对其进行配置,然后调用BuildUp就可以得到最后的产品(要创建的对象),或者调用TearDown来销毁对象,BuildUpTearDown是一对开闭操作,你使用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就是最好的教材。

    本系列作为新年礼物送给大家,希望你们能够喜欢。文稿没有经过校对和润色,有许多错字别字,还望大家多多包涵,如果有什么批评和建议,也请不吝赐教。

    (全文完)


    最新回复(0)