[]Object Builder Application Block文/黄忠成 ;2006/9/21íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
原文链接:http://blog.csdn.net/Code6421/archive/2006/09/25/1282150.aspxíÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
整理:吕震宇íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
三、ObjectBuilder Application BlockObjectBuilder一开始出现于Microsoft所提出的Composite UI Application Block,主司对象的建立及释放工作, 它实现了本文前面所提及的Dependency Injection概念,同时在架构上提供了高度的延展性。运用ObjectBuilder来建立对象,设计师可以透过程序或组态文件,对对象建立与释放的流程进行细部的调整,例如改变对象建立时所调用的Constructor(构造函数),调整传入的参数,于对象建立后调用特定方法等等。鉴于ObjectBuilder的功能逐渐完整,加上社群对于Dependency Injection实现对象的强烈需求,Microsoft正式将ObjectBuilder纳入Enterprise Library 2006中,并修改Caching、Logger、Security、Data Access等Application Block的底层,令其于ObjectBuilder整合,以此增加这些Application Block的延展性。就官方文件的说明,ObjectBuilder Application Block提供以下的功能。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • 允许要求一个抽象对象或接口,ObjectBuilder会依据程序或组态文件的设定,传回一个实体对象。
  • 回传一个既存对象,或是每次回传一个新的对象(多半用于Dependency、Singleton情况,稍后会有详细说明)。
  • 透过特定的Factory建立一个对象,这个Factory可以依据组态文件的设定来建立对象(CustomFactory,隶属于Enterprise Common Library)。
  • 当物件拥有一个以上的构造函数时,依据已有的参数,自动选取兼容的构造函数来建立要求的对象。(Consturctor Injection)
  • 允许对象于建立后,透过程序或组态文件来赋值至属性,或是调用特定的方法。(Setter Injection、Interface Injection)
  • 提供一组Attribute,让设计师可以指定需要Injection的属性,亦或是于对象建立后需要调用的方法,也就是使用Reflection来自动完成Injection动作。
  • 提供IBuilerAware接口,实现此接口的对象,ObjectBuilder会于建立该对象后,调用OnBuildUp或是OnTearDown方法。
  • 提供TearDown机制,按建立对象的流程,反向释放对象。
对于多数读者来说,这些官方说明相当的隐诲,本文尝试由架构角度切入,讨论ObjectBuidler的主要核心概念,再透过实现让读者们了解,该如何使用ObjectBuidler。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
3-1、The Architecture of Object Builder图2íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
图2是ObjectBuilder中四个主要核心对象的示意图,BuidlerContext是一个概念型的环境对象,在这个对象中,包含着一组Strategys对象,一组Polices对象,一个Locator对象, ObjectBuidler采用Strategys Pipeline(策略流)概念,设计师必须透过Strategy串行来建立对象,而Strategy会透过Polices来寻找『类型/id』对应的Policy对象,使用 它来协助建立指定的对象。此处有一个必须特别提出来讨论的概念,Strategy在架构上是与类型无关的,每个BuidlerContext会拥有一群Strategys对象,我们透过这个Strategys对象来建立任何类型的对象,不管建立的对象类型为何,都会通过这个Strategys Pipeline。这意味着,当我们希望于建立A类型对象后调用方法A1,于建立B类型对象后调用方法 B1时,负责调用方法的Strategy对象会需要一个机制来判别该调用那个方法,那就是Policy对象,BuilderContext中拥有一个Polices对象,其中存放着与『类型/id』对应的Policy对象,如图3所示。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
图3íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
值得一提的是,Policy是以Type/id方式,也就是『类型/id』方式来存放,这种做法不只可以让不同类型拥有各自的Policy,也允许同类型但不同id拥有各自的Policy。ObjectBuilder中的最后一个元素是Locator,Locator对象在ObjectBuidler中扮演着前述的Service Locator角色,设计师可以用Key/Value的方式,将对象推入Locator中,稍后再以Key值来取出使用。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
3-2、StrategysObjectBuilder内建了许多Strategy,这些Strategy可以大略分成四种类型,如图4。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
图4íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Pre-Creation Strategy
Pre-Creation意指对象被建立前的初始动作,参与此阶段的Strategy有:TypeMappingStrategy、PropertyReflectionStrategy、ConstructorReflectionStrategy、MethodReflectionStrategy及SingletonStrategy,稍后我们会一一检视 它们。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Creation Strategy
Creation类型的Strategy主要工作在于建立对象,它会利用Pre-Creation Strategys所准备的参数来建立对象,ObjectBuilder就是运用Pre-Creation的ConstructorReflectionStrategy及CreationStrategy来完成Constructor Injection动作。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Initialization Strategy
当对象建立后,会进入初始化阶段,这就是Initialization Strategy阶段,在此阶段中,PropertySetterStrategy会与PropertyReflectionStrategy合作,完成Setter Injection。而MethodExecutionStrategy则会与MethodReflectionStrategy合作,在对象建立后,调用特定的方法,也就是Method Injection(视使用方式,Interface Injection是以此种方式完成的)。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Post-Initialization Strategy
在对象建立并完成初始化动作后,就进入了Post-Initialization Strategy阶段,在此阶段中,BuilderAwareStrategy会探询已建立的对象是否实现了IBuilderAware接口,是的话就调用IBuilderAware.OnBuildUp方法。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • 关于对象释放
先前曾经提过,ObjectBuidler在建立对象时,会一一调用所有Strategy来建立对象,同样的!当释放对象时,ObjectBuilder也会进行同样的动作,不过方向是相反的,在内建的Strategy中,只有BuilderAwareStrategy会参与对象释放的动作,在对象释放时,BuilderAwareStrategy会探询欲释放的对象是否实现了IBuidlerAware接口,是的话就调用IBuidlerAware.OnTearDown方法。 íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
3-3、A Simple Application再怎么详细的说明,少了一个实例就很难让人理解,本节以一个简单的ObjectBuidler应用实例开始,一步步带领读者进入ObjectBuilder的世界。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序10íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System.Collections.Generic; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System.Text; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using Microsoft.Practices.ObjectBuilder; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
namespace SimpleApp íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    class Program íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        static void Main(string[] args) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            Builder builder = new Builder(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            TestObject obj = builder.BuildUp<TestObject>(new Locator(), null, null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            obj.SayHello(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            Console.ReadLine(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public class TestObject íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public void SayHello() íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            Console.WriteLine("TEST"); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
这是一个相当阳春的例子,在程序一开始时建立了一个Builder对象,它是ObjectBuilder所提供的Facade对象,其会预先建立一般常用的Strategy串行,并于BuilderUp方法被调用时,建立一个BuilderContext对象,并将Srategy串行及Polices串行指定给该BuilderContext,然后进行对象的建立工作。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • How Object Creating
要了解前面的例子中,TestObject对象究竟是如何被建立起来的,首先必须深入Builder对象的建构动作。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
private StrategyList<TStageEnum> strategies = new StrategyList<TStageEnum>(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public BuilderBase() íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public PolicyList Policies íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    get { return policies; } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public StrategyList<TStageEnum> Strategies íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    get { return strategies; } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public Builder(IBuilderConfigurator<BuilderStage> configurator) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<CreationStrategy>(BuilderStage.Creation); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    if (configurator != null) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        configurator.ApplyConfiguration(this); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
当Buidler对象被建立时,其构造函数会将前面所提及的几个Strategys加到Strategies这个StrategyList Collection对象中,待BuildUp方法被调用时指定给新建立的BuilderContext对象。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
string idToBuild, object existing, params PolicyList[] transientPolicies) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    return (TTypeToBuild)BuildUp(locator, typeof(TTypeToBuild), idToBuild,  íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        existing, transientPolicies); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public virtual object BuildUp(IReadWriteLocator locator, Type typeToBuild, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        string idToBuild, object existing, params PolicyList[] transientPolicies) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    .................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    return DoBuildUp(locator, typeToBuild, idToBuild, existing, transientPolicies); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
private object DoBuildUp(IReadWriteLocator locator, Type typeToBuild,  íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        string idToBuild, object existing, PolicyList[] transientPolicies) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    IBuilderStrategyChain chain = strategies.MakeStrategyChain(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    .................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    IBuilderContext context = MakeContext(chain, locator, transientPolicies); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ....................                            íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    object result = chain.Head.BuildUp(context, typeToBuild, existing, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    .................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
private IBuilderContext MakeContext(IBuilderStrategyChain chain, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        IReadWriteLocator locator, params PolicyList[] transientPolicies) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    .................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    return new BuilderContext(chain, locator, policies); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
当Builder的泛型方法BuildUp方法被调用后,其会调用非泛型的BuildUp方法,该方法会调用DoBuildUp方法,此处会透过strategies(先前于Builder构造函数时初始化的StrategyList对象)来取得Strategys串行,并指定给稍后由MakeContext方法建立的BuilderContext,最后调用Strategy串行中第一个Strategy的BuildUp方法来进行对象的建立动作。在这一连串的动作中,我们可以厘清几个容易令人混淆的设计,第一!我们是透过Strategy串行,也就是IBuidlerStrategyChain.Head.BuildUp来建立对象,这个Head属性就是Strategy串行中的第一个Strategy。第二!BuilderContext的作用在于,于调用各个Strategy.BuildUp方法时,给予 它们存取此次建立动作所使用的Strategys及Policies等对象的机会。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Policy物件的用途
现在,我们弄清楚了Strategy的用途,BuilderContext的真正涵意,但还有两个元素尚未厘清,其中之一就是Policy对象,前面曾经稍微提过,Strategy是与类型无关的设计概念,因此为了针对不同类型做个别的处理,我们需要另一个与类型相关的设计,那就是Policy对象,要确认这点,必须重返Builder的构造函数。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public Builder(IBuilderConfigurator<BuilderStage> configurator) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    .................. íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ................. íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
这里调用了Policies的SetDefault方法,Policies是一个PolicyList对象,其提供了推入(Set、SetDefault)及取出(Get)方法,允许设计者针对所有『类型/id』及特定『类型/id』指定对应的IBuilderPolicy对象,那这有什么用呢?这个问题可以由CreationStrategy类别中的以下这段程序代码来回答。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    if (existing != null) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        BuildUpExistingObject(context, typeToBuild, existing, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    else íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        existing = BuildUpNewObject(context, typeToBuild, existing, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    return base.BuildUp(context, typeToBuild, existing, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)] íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
private object BuildUpNewObject(IBuilderContext context, Type typeToBuild, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
object existing, string idToBuild) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ICreationPolicy policy = context.Policies.Get<ICreationPolicy>(typeToBuild, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ......................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    InitializeObject(context, existing, idToBuild, policy); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    return existing; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
private void InitializeObject(IBuilderContext context, object existing, string id, ICreationPolicy policy) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ......................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ConstructorInfo constructor = policy.SelectConstructor(context, type, id); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ......................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    object[] parms = policy.GetParameters(context, type, id, constructor); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    .........................              íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    method.Invoke(existing, parms); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
如你所见,CreationStrategy于建立对象时,会由Policies中取出『类型/id』对应的ICreationPolicy对象,接着利用 它来取得ConstructorInfo(构造函数方法),再以GetParameters方法来取得构造函数所需的参数,最后调用此构造函数。这段程序代码告诉我们Policy的真正用途,就是用来协助Strategy于不同『类型/id』对象建立时,采取不同的动作,这也就是说,Strategy与Policy通常是成对出现的。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Locator
最后一个尚未弄清楚的关键元素是Locator,我们于调用Builder的BuildUp方法时,建立了一个Locator对象并传入该方法,这是用来做什么的呢?在ObjectBuilder中,Locator扮演两种角色,第一个角色是提供一个对象容器供Strategy使用,这点可以透过以下程序了解。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public class SingletonStrategy : BuilderStrategy íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public override object BuildUp(IBuilderContext context, Type typeToBuild, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    object existing, string idToBuild) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        DependencyResolutionLocatorKey key = new DependencyResolutionLocatorKey(typeToBuild, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        if (context.Locator != null && context.Locator.Contains(key, SearchMode.Local)) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            TraceBuildUp(context, typeToBuild, idToBuild, ""); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            return context.Locator.Get(key); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        return base.BuildUp(context, typeToBuild, existing, idToBuild); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
SingletonStrategy是一个用来维持某一个对象只能有一份实体存在,当此Strategy被唤起时,其会先至Locator寻找目前要求的对象是否已被建立,是的话就取出该对象并传回。Locator同时也可以作为一个Service Locator,这点可以由以下程序代码来验证。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
locator.Add("Test",new TestObject()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
............. íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
TestObject obj = locator.Get<TestObject>("Test");
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
当然,这种手法有一个问题,那就是TestObject对象是预先建立后放在Locator中,这并不是一个好的设计,后面的章节我们会提出将Service Locator与Dependency Injection整合的手法。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
PSbjectBuidler的Locator离完善的Service Locator还有段距离。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
四、Dependency Injection With ObjectBuilderObjectBuilder支持Dependency Injection中定义的三种Injection模式,本章将一一介绍如何运用ObjectBuilder来实现。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
4-1、Constructor InjectionConstructor Injection的精神在于使用构造函数来进行注入动作,本节延用InputAccept的例子,程序11是改采ObjectBuilder进行Constructor Injection的例子。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序11íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System.Collections.Generic; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System.Text; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using System.Configuration; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using Microsoft.Practices.ObjectBuilder; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
namespace OB_ConstructorInjectionTest íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    class Program íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        static void UseValueParameter(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            ConstructorPolicy creationPolicy = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            creationPolicy.AddParameter(new ValueParameter(typeof(IDataProcessor), new PromptDataProcessor())); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        static void Main(string[] args) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            MyBuilderContext context = new MyBuilderContext(new Locator()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            context.InnerChain.Add(new CreationStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            UseValueParameter(context); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context, typeof(InputAccept), null, null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            accept.Execute(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            Console.Read(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    internal class MyBuilderContext : BuilderContext íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public IReadWriteLocator InnerLocator; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public BuilderStrategyChain InnerChain = new BuilderStrategyChain(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public PolicyList InnerPolicies = new PolicyList(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public LifetimeContainer lifetimeContainer = new LifetimeContainer(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public MyBuilderContext() íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            : this(new Locator()) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public MyBuilderContext(IReadWriteLocator locator) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            InnerLocator = locator; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            SetLocator(InnerLocator); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            StrategyChain = InnerChain; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            SetPolicies(InnerPolicies); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            if (!Locator.Contains(typeof(ILifetimeContainer))) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public class InputAccept íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        private IDataProcessor _dataProcessor; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public void Execute() íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            Console.Write("Please Input some words:"); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            string input = Console.ReadLine(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            input = _dataProcessor.ProcessData(input); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            Console.WriteLine(input); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public InputAccept(IDataProcessor dataProcessor) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            _dataProcessor = dataProcessor; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public interface IDataProcessor íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        string ProcessData(string input); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public class DummyDataProcessor : IDataProcessor íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        #region IDataProcessor Members íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public string ProcessData(string input) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            return input; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        #endregion íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public class PromptDataProcessor : IDataProcessor íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        #region IDataProcessor Members íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        public string ProcessData(string input) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
            return "your input is: " + input; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        #endregion íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序于一开始时,建立了一个MyBuilderContext对象,会自行建立BuilderContext对象而不使用Builder对象的目的很单纯,就是为了厘清个别Strategy究竟做了那些事,这点在使用Builder对象时,会因为内建的Strategy都已加入,而显得有些模糊。在MyBuilderContext对象建立后,此处将一个CreationStrategy加到Strategy串行中,CreationStrategy这个Strategy被归类为Creation阶段,是真正建立对象的Strategy,紧接着UseValueParameter方法会被调用,这个方法中建立了一个ConstructorPolicy对象,并调用其AddParameter方法,加入一个ValueParameter对象,这个ValueParameter对象就对应着InputAccept的构造函数所需的参数,CreationStrategy于对象建立后,会透过BuilderContext的Policies来取得『类型/id』对应的ICreationPolicy对象(本例就是ConstructorPolicy对象),然后调用ICreationPolicy.SelectionConstructor方法,这个方法必须根据调用者已用ICreationPolicy.AddParameter所传入的参数来选择正确的构造函数,然后再调用这个构造函数并填入参数值来完成对象建立工作,图5是这整个流程的示意图。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
图5íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
图中读者可能会有所迷惑的是,FormatterServices.GetSafeUninitializedObject方法是何作用?这是.NET Framework中一个建立对象的途径,与一般new或是Activator.CreateInstance方式不同,GetSafeUninitializedObject方法并不会触发该对象的构造函数,只是单纯的将对象建立起来而已,因此CreationStrategy才必须于最后调用对应的构造函数。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Understanding Parameter
程序11中使用了一个ValueParameter对象,要知道这个对象的作用,我们得先了解Parameter在ObjectBuilder中所代表的意义,在三种注入模式中,有一个共通的规则,就是需要有参数来注入,Constructor Injection是透过构造函数参数注入,而Interface Injection则是透过函数参数注入,Setter Injection则是透过属性注入,因此参数是这三种注入模式都会用到的观念,所以ObjectBuilder定义了IParameter接口,并提供一组实现此接口的参数对象,于注入时期由这些参数对象来取得参数值,如图6。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
图6íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • ValueParameter
这是一个最简单的Paramter对象,构造函数如下所示:íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public ValueParameter(Type valueType, object value)íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
它的GetValue方法仅是将构造函数传入的value对象传回而已。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • DependencyParamter
DependencyParameter是一个功能强大的Parameter对象,程序12是以DependencyParameter来取代ValueParameter完成Constructor Injection的例子。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序12íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseDependencyParameter(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  ConstructorPolicy creationPolicy = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor),null, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
      typeof(PromptDataProcessor),NotPresentBehavior.CreateNew,SearchMode.Local)); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  ConstructorPolicy creationPolicy2 = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  context.Policies.Set<ICreationPolicy>(creationPolicy2, typeof(PromptDataProcessor),null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
读者可以发现,DependencyParameter并未要求建构者传入任何对象实体,而是要求建构者传入注入时对应的参数类型、参数名称、实体类型、NotPersentBehavoir及SearchMode等参数,下面的程序行表是DependencyParameter的构造函数:íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public DependencyParameter(Type parameterType, string name, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
          Type createType, NotPresentBehavior notPresentBehavior, SearchMode searchMode)
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
第一个参数是参数的类型,第二个参数是参数的名称,当ConstructorPolicy于SelectConstructor方法时,会依据这两个参数来选取适合的构造函数,第三个参数是实体对象的类型,以本例来说,就是以PromptDataProcessor这个类型建立对象来传入需要IDataProcessor类型的构造函数、方法或属性,第四个参数则影响了DependencyParameter的取值动作,预设情况下,DependencyParameter会先至Locator中取值,这个动作会受到第五个参数:SearchMode的影响(稍后会介绍这一部份),如果找不到的话,就会依据此参数值来做动作,NotPersentBehavior这个列举的定义如下:íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public enum NotPresentBehavior íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{                    íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  CreateNew, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  ReturnNull, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  Throw, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
CreateNew代表着当DependencyParameter于Locator找不到需要的值时,调用BuilderContext.HeadOfChain.BuildUp方法来建立该对象,以此例来说即是如此,所建立对象的类型就是PromptDataProcessor。ReturnNull则是回传一个Null值,Throw则是直接抛出一个例外。好了,了解了整体流程后,现在让我们一一厘清这个流程中剩下的部份,第一!于Locator找寻需要的值是什么意思,试想一种情况,当我们在做Dependency Injection时,是否有某些欲注入对象是可重用的,也就是该对象可以只建立一个,注入多个不同的对象,让这些对象共享这个注入对象,这就是DependencyParameter会先至Locator中找寻已推入的注入对象的原因,请参考程序13的例子。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序13íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseDependencyParameter(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerLocator.Add(new DependencyResolutionLocatorKey(typeof(IDataProcessor), null),  íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        new PromptDataProcessor()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ConstructorPolicy creationPolicy = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor), nullíÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        typeof(PromptDataProcessor), NotPresentBehavior.CreateNew, SearchMode.Local)); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ConstructorPolicy creationPolicy2 = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.Set<ICreationPolicy>(creationPolicy2, typeof(PromptDataProcessor), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
这个例子预先建立了一个PromptDataProcessor对象,并以DependencyResolutionLocatorKey封装后推入Locator中,这样一来,当DependencyParameter取值时,就会依据参数的『类型/id』至Locator找寻需要的值,此时就会得到我们所推入的PromptDataProcessor对象,而不是建立一个新的,另外!只要于AddParameter所传入的DependencyParameter是以IDataProcessor为参数类型,并以null为id(名称)的话,那么永远都会传回我们所推入Locator的PromptDataProcessor 对象。第二个要厘清的是SearchMode的涵意,在ObjectBuilder的架构上,Locator是可以有Parent/Child关系的,当DependencyParameter要找寻需要的对象时,如果SearchMode是Local的话,那么这个搜寻动作只会搜寻该Locator自身,如果是Up的话,那么在该Locator自身搜寻不到时,就会往Parent Locator搜寻。第三个要厘清的是第二个ConstructorPolicy的建立动作,还记得吗?我们提过Policy是『类型/id』相关的,当DependencyParameter无法于Locator找到需要的对象而透过BuildUp来建立对象时,该『类型/id』同样需要一个ICreationPolicy来对应,否则将会引发Missing Policy的例外,注意!DependencyParameter所使用的name参数必须与设定Set<ICreationPolicy>时所传入的第三个参数相同。最后一个问题是,如果每个『类型/id』都要设定对应的ICreationPolicy,岂不累人,ObjectBuilder当然没有这么不人性化,我们可以调用Policies.SetDefault来为所有『类型/id』预设一个ICreationPolicy,如程序14所示。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序14íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseDependencyParameter(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ConstructorPolicy creationPolicy = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor),  íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        null, typeof(PromptDataProcessor), NotPresentBehavior.CreateNew, SearchMode.Local)); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.SetDefault<ICreationPolicy>(new ConstructorPolicy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • CreationParameter
与DependencyParameter相同,CreationParameter也会透过BuildUp来建立对象,不同的是其不会先搜寻Locator,也无法作参数类型与实体类型对应,因此无法适用于InputAccept这种以接口为介质的注入方式,必须与TypeMappingStrategy(后述)合用才能解决,如程序15所示。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序15íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseCreationParameter(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ConstructorPolicy creationPolicy = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    creationPolicy.AddParameter(new CreationParameter(typeof(IDataProcessor))); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    TypeMappingPolicy mappingPolicy = new TypeMappingPolicy(typeof(PromptDataProcessor), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.Set<ITypeMappingPolicy>(mappingPolicy, typeof(IDataProcessor), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.SetDefault<ICreationPolicy>(new ConstructorPolicy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void Main(string[] args) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    MyBuilderContext context = new MyBuilderContext(new Locator()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerChain.Add(new TypeMappingStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerChain.Add(new CreationStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    UseCreationParameter(context); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context, íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        typeof(InputAccept), null, null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    accept.Execute(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    Console.Read(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • CloneParameter
CloneParameter的构造函数接受一个IParameter参数,当其GetValue方法被调用时,会透过从构造函数指定的Parameter对象来取值,如果取得的值是实现了ICloneable接口的对象时,其将调用Clone方法来拷贝该值,否则传回原值,下面的程序片断是CloneParametr的构造函数声明。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public CloneParameter(IParameter param)íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • LookupParameter
LookupParameter的构造函数接受一个object类型的参数,当GetValue方法被调用时,会经由Locator.Get方法,以构造函数所传入的参数为键值,取得位于Locator中的值,下面的程序片断为LookupParameter的构造函数声明。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public LookupParameter(object key)íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序16则是将InputAccept范例改为使用LookupParameter的版本。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序16íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseLookupParameter(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerLocator.Add("dataProcessor", new PromptDataProcessor()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ConstructorPolicy creationPolicy = new ConstructorPolicy(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    creationPolicy.AddParameter(new LookupParameter("dataProcessor")); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.Policies.SetDefault<ICreationPolicy>(new ConstructorPolicy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • InjectionConstructor Attribute
使用Paramerer对象来进行Consturctor Injection时,设计者必须在建立对象前,预先准备这些Parameter对象,虽然动作不算繁锁,但若全部对象的建立都要这么做,未免有些没有效率,为此!ObjectBuilder提供了另一种较为简单的方法,就是利用InjectionConstructor这个Attribute,再搭配上ConstructorReflectionStrategy对象,自动的为设计者准备这些Parmeter对象,程序17是修改为InjectionConstructor模式的版本。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序17íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseInjectionConstructorAttribute(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerChain.Add(new ConstructorReflectionStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerChain.Add(new CreationStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
.......... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public
class InputAccept íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    private IDataProcessor _dataProcessor; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public void Execute() íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        Console.Write("Please Input some words:"); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        string input = Console.ReadLine(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        input = _dataProcessor.ProcessData(input); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        Console.WriteLine(input); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    [InjectionConstructor] íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    public InputAccept([Dependency(Name = "dataProcessor", íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        CreateType = typeof(PromptDataProcessor))]IDataProcessor dataProcessor) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    { íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        _dataProcessor = dataProcessor; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    } íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
要使用InjectionConstructor Attribute,我们必须在CreationStrategy这个Strategy前加入一个ConstructorReflectionStrategy对象, 它会于建立对象动作时,探询欲建立对象类型所提供的所有构造函数,选取已标上InjectionConstrucor Attribute的那个为指定构造函数,接着ConstructorReflectionStrategy会探询该构造函数的所有参数,查看是否标上Dependency Attribute,是的话就以其设定建立DependencyParameter,否则建立一个新的DependencyParameter,它会单以类型参数来建立DependencyParameter,最后ConstructorReflectionStrategy会以这些信息来建立对应的ConstructorPolicy对象,完成整个对象建立动作。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Understanding Dependency Attribute
ConstructorReflectionStrategy依赖两个关键的Attribute,一个是用来标示指定构造函数的InjectionConstructor Attribute,另一个则是用来标示参数该如何取得的Dependency Attribute,此Attribute有四个属性,分别对应到DependencyParameter的四个属性:íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
DependencyAttributeDependencyParameter说明
NameNameid(名称)
CreateTypeCreateType欲建立对象的实体类型
NotPersentBehaviorNotPersentBehavior当欲建立对象无法由Locator取得时的行为模式。
SearchModeSearchMode对Locator的搜寻法则。
使用Dependency Attribute与ConsturctorReflectionStrategy模式的优点是设计者不需花费时间一一建立Parameter对象,而缺点就是CreateType参数,由于ConstructorReflectionStrategy依赖着Dependency Attribute的CreateType参数来决定实际建立对象的类型,这使得设计者必须在标示Dependency Attribute时,一并指定这个参数,否则ConstructorReflectionStrategy将会以参数类型做为建立实际对象时的类型,而在本例中,我们无法建立一个IDataProcessor对象,这点降低了程序的可订制性。那这要如何解决呢?简单的方法是撰写一个新的Dependency Attribute、或是使用TypeMappingStrategy,复杂的则是撰写一个新的ConstructorReflectionStrategy,后面的章节我们会再重访这个问题。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • Injection with DependencyResolutionLocator
前面谈到DependencyParameter时曾经提过,它会先至Locator中搜寻需要的参数值,那么这也意味着,在使用ConstructorReflectionStrategy时,我们可以将参数值先行推入Locator中,这样就可以避开指定CreateType了,如程序18所示。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
程序18íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
static void UseDependencyResolution(MyBuilderContext context) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerChain.Add(new ConstructorReflectionStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerChain.Add(new CreationStrategy()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    context.InnerLocator.Add(new DependencyResolutionLocatorKey(typeof(IDataProcessor), íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        "dataProcessor"), new PromptDataProcessor()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
} íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
[InjectionConstructor] íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public InputAccept([Dependency(Name = "dataProcessor")]IDataProcessor dataProcessor) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ................... íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
当然,这仍然会有一个问题,那就是必须预先建立PromptDataProcessor对象,而非于InputAccept对象建立时期建立,这是在不撰写自定Dependency Attribute或Strategy,亦或是使用TypeMappingStrategy情况下的简易解法。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
  • DefaultCreationPolicy and ConstructorPolicy
ObjectBuilder内建了两个ICreationPolicy的实现对象,一是前面所使用的ConstructorPolicy,二是DefaultCreationPolicy,与ConstructorPolicy不同,DefaultCreationPolicy永远使用预设的构造函数,如下所示。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public ConstructorInfo SelectConstructor(IBuilderContext context, Type type, string id) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    if (constructor != null) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        return constructor; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    List<Type> types = new List<Type>(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    foreach (IParameter parm in parameters) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        types.Add(parm.GetParameterType(context)); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    return type.GetConstructor(types.ToArray()); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
}
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
而调用该构造函数时所需的参数,则直接以BuildUp方法,依据参数的『类型/id』来建立,没有与Parameter的互动。íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
public object[] GetParameters(IBuilderContext context, Type type, string id, ConstructorInfo constructor) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
{ íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    ParameterInfo[] parms = constructor.GetParameters(); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    object[] parmsValueArray = new object[parms.Length]; íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
    for (int i = 0; i < parms.Length; ++i) íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
        parmsValueArray = context.HeadOfChain.BuildUp(context, parms.ParameterType, null, id); íÉ ò±?Cçwww.netcsharp.cnMá!¿WeëßfÈ
íÉ