自然而然的Object Builder 应用过程(一)

  兄弟,你可能从来没听说过Object Builder,它很老了,但却是CAB构建对象的核心,而CAB框架协助开发人员在桌面程序中使用MVC,提高开发效率。CAB最大限度缓解了NET程序员的痛苦。为了程序员忍受的痛苦,为了程设的无奈,为了无尽的调试和混乱的逻辑,真心-------希望你能看这整篇枯涩文档。

    Object Builder是个简单的框架,但赋予我们构建复杂对象实例的能力,若专业地讲Object Builder是“通过策略和配置信息自动创建对象实例的对象构造器”。在很多场合下Object Builder被认为是IOC模式的微软实现。有人认为它太“轻”,实际上正是因为这种“轻”才为开发者提供了强大的扩展性。人们在设计中常有这样的认识,“重”的框架要比“轻”的框架更稳妥,功能更强,会给开发者提供更多的方便,能更迅捷的完成开发。但在实际工程中,我们发现使用不当的“重”框架也能给我们带来更“重”的麻烦。同是“学院派”的设计,人们既然能容忍Enterprise Library之“重”,可为什么不能接受Object Builder之“轻”呢。

    让我们开始理解Object Builder之旅吧。

     对象,对象,都是对象。在NET程序中对象无处不在。我们的程序实际上就是引入类、创建对象实例、使用对象实例、销毁对象实例的过程。对象大到From UI 小到一个Point或Size,我们无时无刻不在呵护它们,稍不留意,错误就接踵而至。在某些极端情况下,当我们所定义的业务逻辑复杂到一定程度-----对象嵌着对象,数据套着数据,而我们的设计还要考虑灵活性和松耦合时,梦魇将由此开始,而创建这些复杂的对象实例又是噩梦中的噩梦。正是鉴于此GOF23定义了Builder设计模式,也称为创建者模式。通过使用创建者模式,我们能精细而巧妙地构建对象的各部分,最后在黑箱中组装完成,将具有完整功能的对象实例提供给使用者。Microsoft通过Object Builder实现了Builder模式,为我们构建复杂对象提供了便捷的工具,也让我们得以一窥框架设计大师们的风范。

要谈Object Builder就不得不了解Microsoft的模式与实践小组(P&P)。Object Builder最早出现在P&P 小组的Composite UI Application Block (CAB)中,通过创建者设计模式和责任链模式构建WorkItem对象。Object Builder诞生不久,即被分离出来,引入Enterprise Library2006中,成为创建复杂对象实例的标准实现。至此Object Builder就存在于P&P的标准体系中,并不断的进化发展,日臻完善。

      同P&P小组的CAB、SCSF、WCSF以及Enterprise Library一样,Object Builder也带有浓郁的“学院”风格,它更像是IOC在学术领域的实现,而非工程领域实现。其实我更倾向于认为,P&P设计Object Builder的目的是为了与其它IOC框架相抗衡。在2005--2006年正是JAVA的Spring呼风唤雨之时,P&P小组也设计了Object Builder。在P&P的设计中也充分的体现了Microsoft “永远借鉴别人而成功” 的一贯风格。

在业务逻辑的框架设计中,我们将Object Builder定义为创建对象实例的核心工具,嵌入到到框架中,通过它分步骤、分阶段地建立“大”的业务逻辑对象实例。我们要得到业务逻辑对象时,通过调用Object Builder的BuildUp获得对象实例。当业务逻辑对象使用完成后,我们通过TearDown来析构对象实例。在Object Builder创建和析构对象的过程中,运用控制Strategy和Polices的手段,来控制、调整对象实例创建、析构流程,并使用一系列的Attribute实现初始化参数的依赖注入。至于 BuildUp返回的对象实例到底是什么、如何构建之类的问题,早已封闭于黑箱之内。我们唯一要做得可能就是用GetType().Isxxxxxx进行些类型检查罢了。

      Object Builder不但具有与其它Microsoft的作品一样容易上手、易用的特点,也继承了P&P的传统——为广大使用者提供了完整的源代码和测试脚本,我认为,正是由于这些原因Object Builder不但是一个精巧的框架,也是学习设计模式的典范。

平心而论P&P在设计Object Builder框架时,侧重于如何“精细”的构建/装配实例。这就决定了它适合于构建复杂对象。若要构建Point或Size之类的简单实例是万万用不到Object Builder的。再多说一句在Enterprise Library4.1中Object Builder 2/Unity已经被引入,不过其实质依然是责任链方式。

Object Builder的世界观

     从全局角度来看Object Builder就是一个可控的new ,一个应用了责任链模式的创建者模式实现。下图在应用层面表述了构成Object Builder的对象及其关系。

2010050414104220.gif

 

该示意图虽没有将Object builder中的对象继承关系表现出来,但基本的类都已得到了展现,虽然纵深不够,但还是表现了类的横向关系。下面就用通过一个简单的helloWord例子来观察Object builder创建对象实例的具体步骤。

首先,我们先来定义一个要通过Object builder创建的对象TestObject。

namespace TestApp

{

public class TestObject

    {

        public void ShowHello()

        {

            Console.WriteLine("hello Word");

        }

   }

}

这个类非常简单,但足以说明问题。在以后的篇幅中,我们还会通过不断的完善TestObject类来展示Object builder是如何将参数注入到创建的对象实例中的。

TestObject的功能及其简单,通过定义方法ShowHello()在控制台上显示问候串。需要提醒注意的是TestObject未明确的定义构造函数,所以它具备一个默认的构造函数,这一点在以下的例子中非常重要。

建立主程序

using System;

using System.Collections.Generic;

using System.Text;

using Microsoft.Practices.ObjectBuilder;

namespace TestApp

{

    class TestApp

    {

        static void Main(string[] args)

        {

            Builder Testbuilder = new Builder();

            TestObject obj = Testbuilder.BuildUp<TestObject>(new Locator(), null, null);

            obj.SayHello();

            Console.ReadLine();

        }

    }

}

这里 Microsoft.Practices.ObjectBuilder是Object builder的命名空间,在应用中通过引入Microsoft.Practices.ObjectBuilder.dll获得。我们实例化了Builder类,并通过调用它的BuildUp<TestObject>获得TestObject实例。

其实无论我们要实例化的类有多么复杂,在Object builder创建步骤都是相同的。

  1. 实例化了Builder类。
  2. 针对要创建的类初始化设定Builder。
  3. 通过TestObject obj = Testbuilder.BuildUp<TestObject>(new Locator(), null, null)形式获得创建完成的对象实例。

下面就让我同大家一起入Object builder的世界看看对象实例是如何被创建出来的。

在我们建立的TestApp程序中,最突出的对象是Builde类型的Testbuilder,它是实例的“出站口”。在Object builder中Builde负责引导我们创建对象,程序中使用Object builder时,一般都要建立Builder类实例。Builder是BuilderBase派的生类,Builder通过对BuilderBase的扩展,在内部建立起默认Strategy列表,并调用一系列Strategy类的BuilderUp函数形成责任链创建对象实例。我们现在就把Builde拆开,来看看Microsoft的大师们是如何实现这个对象的。

Builde的继承关系如图

2010050414170350.gif

Builde是一个实现了IBuilder接口的BuilderBase的派生类。接口定义IBuilder如下

public interface IBuilder<TStageEnum>

     {

          PolicyList Policies { get; }//返回Policy的列表。

         StrategyList<TStageEnum> Strategies { get; }//返回Strategy的列表。

         //调用责任链创建类

         object BuildUp(IReadWriteLocator locator, Type typeToBuild, string idToBuild, object existing,

                             params PolicyList[] transientPolicies);

          //调用责任链创建类的泛型方法,我们往往使用这个函数调用来获得对象。

TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator, string idToBuild, object existing,params PolicyList[] transientPolicies);

         //反向调用责任链析构对象

         TItem TearDown<TItem>(IReadWriteLocator locator, TItem item);

     }

基类BuilderBase实现了大部分Object builder创建对象实例的功能。在设计中经常从BuilderBase中派生自定义类,来获得更简约的Builder。

我们应注意BuilderBase的一个重载的构造函数。函数说明如下

public BuilderBase(IBuilderConfigurator<TStageEnum> configurator)

         {

              configurator.ApplyConfiguration(this);

         }

该构造函数引入了实现IBuilderConfigurator接口的configurator参数。IBuilderConfigurator接口必须由开发者自己实现,引入configurator的动机是为开发者提供一种在外部配置BuilderBase的手段。IBuilderConfigurator接口只包含一个函数ApplyConfiguration(IBuilder<TStageEnum> builder)。参数builder是实现IBuilder<TStageEnum>接口的类,在实际中就是BuilderBase的实现类,所以我们可以把实现了IBuilderConfigurator接口的实例,作为configurator参数,传入BuilderBase(IBuilderConfigurator<TStageEnum> configurator)构造函数,在外部实现对BuilderBase的控制。其代码如下

 class TestBuilderConfigurator : IBuilderConfigurator<BuilderStage>

    {

        #region IBuilderConfigurator<TStageEnum> 成员

        public void ApplyConfiguration(IBuilder<BuilderStage> builder)

        {

            builder.Strategies.AddNew<MyTestStrategy>(BuilderStage.Initialization);

            builder.Strategies.AddNew<ObjectFindStrategy>(BuilderStage.Initialization);

            builder.Policies.SetDefault<IMyTestPolicy>(new MyTestPolicy("hello my test"));

            builder.Policies.SetDefault<ObjectFindPolicy>("gnbyns");

        }

        #endregion

}

在实例化BuilderBase时代码如下

Builder testBuilder = new Builder (new TestBuilderConfigurator());

通过以上IBuilderConfigurator实现类TestBuilderConfigurator,修改了testBuilder内部的Strategies与Policies,从而起到了在外部定制BuilderBase的目的。而Builder的缺省构造函数也是调用带有IBuilderConfigurator参数的构造函数来实现的,只不过传递的是null值。

public Builder()

: this(null){}

BuilderBase包含了Policies和Strategies属性,它们分别暴露PolicyList和StrategyList类型的私有字段。而PolicyList和StrategyList类,通过被它们包装的Dictionary记录了Builder中将被使用的Policy集合和Strategy集合。

通俗的讲,在Object builder中Strategy决定了 “在什么位置上通过那个Policy建立或初始化对象”。Policy决定了“在Strategy定义的某个点上如何建立或初始化对象”。所以BuilderBase中Policies和Strategies属性就包含了构建对象的全部信息,我们在实际工程中要“修剪”的就是Policies和Strategies两个属性。

BuilderBase中Strategies属性是StrategyList<TStageEnum>类型。TStageEnum实际上就是BuilderStage枚举。具体如下:

     public enum BuilderStage

     {

         PreCreation,//准备创建阶段

         Creation,//创建阶段

         Initialization,//创建对象后进行初始化阶段

         PostInitialization//创建最后完成阶段

     }

BuilderStage的作用就是说明某个Strategy作用在那个创建的阶段,起到在BuilderBase中锚定Strategy的作用。这里还有必要再次明确两个类Strategy和Policy。打个比方,它们之间有点像螺杆和螺帽的关系,Strategy像“螺钉”被BuilderStage固定在BuilderBase构建对象的不同阶段,对将被构建的对象实例,Strategy只起到了“地标”的作用,只是标定了在构建对象过程中的一个个点,至于这些点到底要做什么,则是由拧在Strategy上的“螺帽”Policy决定的。若某个Strategy螺杆上没有螺帽Policy,就是一个“空白点”,这个点将被跳过,不会产生任何实质的动作,但有时也存在意外,在某个Strategy中完成了所有的工作,而不需要引入某个Policy。

Object builder正是通过BuilderStage实现对责任链设计模式中“链”的控制。StrategyList<TStageEnum>关系如图

2010050414180378.gif

public class StrategyList<TStageEnum>

     {

         private readonly static Array stageValues = Enum.GetValues(typeof(TStageEnum));

//获得创建阶段的常数枚举值

         private Dictionary<TStageEnum, List<IBuilderStrategy>> stages;

         private object lockObject = new object();

静态的只读Array类型stageValues参数,包含了在BuilderStage枚举中定义的所有值。其顺序在BuilderStage中已经定义完成,这就决定了我们的Strategy具有从PreCreation到PostInitialization的排列顺序。object lockObject只是一个标注,通过lock (lockObject)使StrategyLis具有线程内的安全性。

StrategyList的构造函数初始化了它内部的 Dictionary<TStageEnum, List<IBuilderStrategy>> stages;字段,stages是List<IBuilderStrategy>的一个容器。

         public StrategyList()

         {

              stages = new Dictionary<TStageEnum, List<IBuilderStrategy>>();

              foreach (TStageEnum stage in stageValues)

 {

//建立一个以BuilderStage枚举值为键值的列表字典

              stages[stage] = new List<IBuilderStrategy>();

 }

         }

当StrategyList构建完成后内部的stages结构如下

Key

Value 

PreCreation

List<IBuilderStrategy>

Creation

List<IBuilderStrategy>

Initialization

List<IBuilderStrategy>

PostInitialization

List<IBuilderStrategy>

stages的Value中保存的是IBuilderStrategy接口对象的List。由于在List类型中成员的顺序由成员插入顺序决定,就产生了一个隐蔽的问题,即在每个BuilderStage阶段插入Strategy的顺序将决定了BuilderBase调用Strategy的顺序,这是我们在开发中应注意的问题。

     public void Add(IBuilderStrategy strategy, TStageEnum stage)

         {    lock (lockObject)

              {stages[stage].Add(strategy);}

}

         public void AddNew<TStrategy>(TStageEnum stage)where TStrategy : IBuilderStrategy, new()

         {lock (lockObject)

              {stages[stage].Add(new TStrategy());}

         }

StrategyList中的Add方法将一个IBuilderStrategy的实现添加到BuilderStage的某个阶段中。因为被lock (lockObject)包装,所以Add方法具备了线程内安全。AddNew是个泛型方法,并通过where TStrategy : IBuilderStrategy, new()进行了约束。

         public void Clear()

         {lock (lockObject)

              {foreach (TStageEnum stage in stageValues)

              stages[stage].Clear();}}

StrategyList中的Clear()方法将清空stages,它同样具备线程内安全。

public IBuilderStrategyChain MakeStrategyChain()

         {

              lock (lockObject)

              {

                   BuilderStrategyChain result = new BuilderStrategyChain();

                   foreach (TStageEnum stage in stageValues)

                       result.AddRange(stages[stage]);

                   return result;

              }

         }

StrategyList的MakeStrategyChain是较重要的方法,它返回了BuilderStrategy的正向链表。IBuilderStrategyChain及其实现BuilderStrategyChain具体如下

2010050414192540.gif

BuilderStrategyChain中strategies是个 List<IBuilderStrategy>,包含了IBuilderStrategy的所有实现。Head返回的是strategies中的strategies[0],就是第一个Strategy,是链表的头。AddRange(IEnumerable strategies)将实现了IEnumerable 接口strategies 拼接到strategies的结尾处。GetNext(IBuilderStrategy currentStrategy)通过遍历,返回链表中currentStrategy之后的下一个IBuilderStrategy实现。

StrategyList的MakeStrategyChain给我们提供了根据BuilderStage排序的List<IBuilderStrategy>,为责任链模式提供了链表。它同样是线程内安全。

     public IBuilderStrategyChain MakeReverseStrategyChain()

         {

              lock (lockObject)

              {

                   List<IBuilderStrategy> tempList = new List<IBuilderStrategy>();

                   foreach (TStageEnum stage in stageValues)

                       tempList.AddRange(stages[stage]);

                   tempList.Reverse();

                   BuilderStrategyChain result = new BuilderStrategyChain();

                   result.AddRange(tempList);

                   return result;

              }

         }

MakeReverseStrategyChain()用Reverse()提供了一个反向构建的链表,其目的是在析构对象实例时应用它实现Strategy的反向操作。

BuilderBase中Policies属性是PolicyList类型。PolicyList相对于StrategyList结构比较简单。具体如下

2010050414201678.gif

PolicyList的构造函数传入了可变数目参数,并调用AddPolicies将他们添加到类型为Dictionary<BuilderPolicyKey, IBuilderPolicy>的 policies中,AddPolicies同样是线程内安全。

         public PolicyList(params PolicyList[] policiesToCopy)

         {

              if (policiesToCopy != null)

                   foreach (PolicyList policyList in policiesToCopy)

                       AddPolicies(policyList);

         }

BuilderPolicyKey是一个由policyType、typePolicyAppliesTo、idPolicyAppliesTo构成的简单结构,而非一个类,所以BuilderPolicyKey是“传值”而非“传引用”,BuilderPolicyKey起到了一个复合Key的作用。在PolicyList中的核心方法往往都需要个BuilderPolicyKey键。

PolicyList 的属性Count是一个具备线程安全的只读属性,返回了PolicyList中包含Policy的个数。

PolicyList 的方法Clear、ClearAll、ClearDefault负责清除PolicyList中的Policy,他们同样具备线程安全性。

PolicyList 的一系列Get方法为在Strategy中获得某个Policy提供了手段,同样他们也具备线程安全性。

PolicyList 的一系列Set方法实现了向PolicyList中插入Policy。实际开发中PolicyList的Get方法和Set方法我们使用得非常频繁。

BuilderBase中Policies和strategies属性为创建对象准备了材料,而用材料创建实际对象实例,是由BuilderBase的核心方法BuildUp实现的。

BuildUp方法有两个实现版本,其中一个实现了泛型的应用,具体不需要过多的解释,只是通过强制类型转化获得的一个强的类型。方法声明如下:

public TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator,

                                          string idToBuild,

object existing, params PolicyList[] transientPolicies)

         {return (TTypeToBuild)BuildUp(locator, typeof(TTypeToBuild), idToBuild, existing, transientPolicies);}

而非泛型版本是实现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 通过判断locator是否为空, locator存在将在lock的保护下调用帮助函数,若为空直接调用帮助函数。BuildUp方法具有了5个参数,参数说明见下表

参数名称

类型

参数的作用

构建方式

locator

IReadWriteLocator

在Object builder中Locator是一个相当重要的对象,起到了管家的作用,在以后会详细讨论。现在我们可以把它认为是一个缓存,用来暂时保存临时的数据,Object builder中主要用它来保存构造时的参数。

Locator类型是IReadWriteLocator的基本实现,我们往往通过使用它就足以完成任务。

  1. 通过new Locator()一个Locator实例,进行设置后带入方法。
  2. 在构造函数内直接new一个Locator的派生,而不需要任何设定

typeToBuild

Type

我们要构建的对象实例的类型

typeof(TestObject)

idToBuild

String

主导着ObjectBuilder的类型识别和对象识别,实现通过ID区分对象创建。

 

existing

object

已经存在的typeToBuild类型对象

 

transientPolicies

PolicyList

应用临时的可变数目参数Policy集合构建对象。

 

其实Object builder中的大部分功能都是围绕着这5个参数来实现的,这些参数就是创建对象实例的五个要素。BuildUp的帮助函数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);

              //获得Object builder的跟踪信息配置

              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;

         }

DoBuildUp方法先通过MakeStrategyChain()获得正向的Strategy列表chain,ThrowIfNoStrategiesInChain实现了对chain的异常检查,MakeContext创建了上下文环境context,然后通过chain.Head的方法BuildUp(context, typeToBuild, existing, idToBuild)启动了责任链,返回了创建的对象实例。

BuilderBase中的TearDown<TItem>方法的帮助方法DoTearDown与DoBuildUp及其相似,只是通过MakeReverseStrategyChain()获得了反向的列表,然后调用chain.Head.TearDown(context, item)析构对象。

在DoBuildUp方法以及DoTearDown中,我们会注意到它们都通过MakeContext(chain, locator, transientPolicies)创建了BuilderContext类型的对象context。

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);

         }

 

....................先到这儿.......................................

转载于:https://www.cnblogs.com/symbols/archive/2010/05/04/objectBilder.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值