雨中的阳光Seattle

靠着很近的Redmond

[转][黄忠成]Object Builder Application Block (3) 2007年09月10日

Filed under: C#,dotNet,IoC,Object Builder — systembug @ 3:19 上午

DependencyResolutionLocatorKey包装该对象实体,放入Locator中,如下所示。

private void RegisterObject(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
if (context.Locator != null)
    {
        ILifetimeContainer lifetime = context.Locator.Get<ILifetimeContainer>(
typeof(ILifetimeContainer), SearchMode.Local);
if (lifetime != ;null)
        {
            ISingletonPolicy singletonPolicy = context.Policies.Get<ISingletonPolicy>(
                typeToBuild, idToBuild);
if (singletonPolicy != null && singletonPolicy.IsSingleton)
            {
                context.Locator.Add(new DependencyResolutionLocatorKey(
                    typeToBuild, idToBuild), existing);
                lifetime.Add(existing);
//……………….
            }
        }
    }
}

以上流程是当该对象实体尚未建立时的流程,假如以BuildUp建立的对象已经存在于Locator中,那么SingletonStrategy的BuildUp方法将直接传回Locator中的对象实体。

public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
    DependencyResolutionLocatorKey key = new DependencyResolutionLocatorKey(
        typeToBuild, idToBuild);
if (context.Locator != null && context.Locator.Contains(key, SearchMode.Local))
    {
        TraceBuildUp(context, typeToBuild, idToBuild, “”);
return context.Locator.Get(key);
    }
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}

PS:注意,SingletonStrategy在该对象已经存在于Locator中时,是直接回传,并不会调用后面如MethodExecutionStrategy、PropertySetterStrategy等Strategy。

5-2、TypeMappingStrategy

前面的章节早已使用过TypeMappingStrategy这个对象了,它主要负责『类型/id』的对应,例如将IDataProcessor接口类型的建立,替换成PromptDataProcessor类型,如程序28所示。

程序28

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_TypeMappingTest
{
class Program
    {
static void Main(string[] args)
        {
            MyBuilderContext context = new MyBuilderContext();
            context.InnerChain.Add(new TypeMappingStrategy());
            context.InnerChain.Add(new CreationStrategy());
            ITypeMappingPolicy policy = new TypeMappingPolicy(typeof(TestObject), null);
            context.Policies.Set<ITypeMappingPolicy>(policy, typeof(ITestInterface), null);
            context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
            ITestInterface obj1 = (ITestInterface)context.HeadOfChain.BuildUp(
                context, typeof(ITestInterface), null, null);
            obj1.SayHello();
            Console.Read();
        }
    }
internal class MyBuilderContext : BuilderContext
    {
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
            : this(new Locator())
        {
        }
public MyBuilderContext(IReadWriteLocator locator)
        {
            InnerLocator = locator;
            SetLocator(InnerLocator);
            StrategyChain = InnerChain;
            SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
        }
    }
public interface ITestInterface
    {
void SayHello();
    }
public class TestObject : ITestInterface
    {
public void SayHello()
        {
            Console.WriteLine(“TEST”);
        }
    }
}

TypeMappingStrategy必须搭配TypeMappingPolicy对象使用,TypeMappingPolicy是一个实现ITypeMappingPolicy接口的对象,构造函数声明如下。

public TypeMappingPolicy(Type type, string id)

第一个参数是映像的实体类型,以本例来说就是TestObject,第二个参数是识别id,接着将其加入context.Policies中,如下所示。

context.Policies.Set<ITypeMappingPolicy>(policy, typeof(ITestInterface), null)

当TypeMappingStrategy的BuildUp方法被调用时,它会以『类型/id』取得对应的ITypeMappingPolicy对象,透过它来取得对应的类型,之后将使用这个类型调用下一个Strategy的BuildUp方法,这就是Type Mapping的流程。

PS:注意,Type Mapping类型必须兼容,如接口->实现、基础类别->衍生类别。

5-3、BuildAwareStrategy

BuildAwareStrategy可以于实现IBuilderAware接口对象建立或释放时,调用对应的OnBuildUp或OnTearDown方法,如程序29所示。

程序29

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_BuildAwareTest
{
class Program
    {
static void Main(string[] args)
        {
            MyBuilderContext context = new MyBuilderContext();
            context.InnerChain.Add(new CreationStrategy());
            context.InnerChain.Add(new BuilderAwareStrategy());
            context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
            TestObject obj = (TestObject)context.HeadOfChain.BuildUp(context,
typeof(TestObject), null, null);
            context.HeadOfChain.TearDown(context, obj);
            Console.Read();
        }
    }
internal class MyBuilderContext : BuilderContext
    {
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
            : this(new Locator())
        {
        }
public MyBuilderContext(IReadWriteLocator locator)
        {
            InnerLocator = locator;
            SetLocator(InnerLocator);
            StrategyChain = InnerChain;
            SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
        }
    }
public class TestObject : IBuilderAware
    {
        #region IBuilderAware Members
public void OnBuiltUp(string id)
        {
            Console.WriteLine(“Object is build up”);
        }
public void OnTearingDown()
        {
            Console.WriteLine(“Object is TearDown”);
        }
        #endregion
    }
}

与其它的Strategy对象不同,BuilderAwareStrategy并不需要Policy对象的协助,它只是判断建立的对象是否实现了IBuilderAware接口。

5-4、BuildUp的第三、四个参数

截至目前为止,我们的例子在调用BuildUp方法时,第三及四个参数都传入null,这两个参数的用途究竟为何呢?这要先从BuildUp方法的宣告谈起。

object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild);

当我们于调用BuildUp方法指定existing为一对象实体时,CreationStrategy将不会建立任何新的对象,只会进行Singleton模式对象的相关动作,然后就调用下一个Strategy对象的BuildUp方法,简单的说!在CreationStrategy后的Strategy仍然会运行,例如Method Injection、Setter Injection都会再次运行,程序30可以协助读者理解这两个参数的用途。

程序30

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_ExistingTest
{
class Program
    {
static void Main(string[] args)
        {
            MyBuilderContext context = new MyBuilderContext();
            context.InnerChain.Add(new CreationStrategy());
            ConstructorPolicy policy = new ConstructorPolicy(new ValueParameter(typeof(string), “id”));
            context.Policies.Set<ICreationPolicy>(policy, typeof(TestObject), null);
            TestObject obj = (TestObject)context.HeadOfChain.BuildUp(context,
typeof(TestObject), null, null);
            TestObject obj2 = (TestObject)context.HeadOfChain.BuildUp(context,
typeof(TestObject), obj, null);
if (obj == obj2)
                Console.WriteLine(“is same object.”);
            Console.Read();
        }
    }
internal class MyBuilderContext : BuilderContext
    {
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
            : this(new Locator())
        {
        }
public MyBuilderContext(IReadWriteLocator locator)
        {
            InnerLocator = locator;
            SetLocator(InnerLocator);
            StrategyChain = InnerChain;
            SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
        }
    }
public class TestObject
    {
private string _id;
public string ID
        {
get
            {
return _id;
            }
        }
public TestObject(string id)
        {
            _id = id;
        }
    }
}

BuildUp的第四个参数则主导着ObjectBuilder的类型识别及对象识别机制,请先看程序31的例子。

程序31

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_IDTesting
{
class Program
    {
static void Main(string[] args)
        {
            MyBuilderContext context = new MyBuilderContext();
            context.InnerChain.Add(new CreationStrategy());
            context.InnerChain.Add(new PropertySetterStrategy());
            context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
            PropertySetterInfo pi1 = new PropertySetterInfo(“ID”, new ValueParameter<string>(“ID1”));
            PropertySetterPolicy pp1 = new PropertySetterPolicy();
            pp1.Properties.Add(“ID”, pi1);
            context.Policies.Set<IPropertySetterPolicy>(pp1, typeof(TestObject), “TO1”);
            PropertySetterInfo pi2 = new PropertySetterInfo(“ID”, new ValueParameter<string>(“ID2”));
            PropertySetterPolicy pp2 = new PropertySetterPolicy();
            pp2.Properties.Add(“ID”, pi2);
            context.Policies.Set<IPropertySetterPolicy>(pp2, typeof(TestObject), “TO2”);
            TestObject obj1 = (TestObject)context.HeadOfChain.BuildUp(context, typeof(TestObject),null, “TO1”);
            TestObject obj2 = (TestObject)context.HeadOfChain.BuildUp(context, typeof(TestObject),null, “TO2”);
            Console.WriteLine(obj1.ID);
            Console.WriteLine(obj2.ID);
            Console.Read();
        }
    }
internal class MyBuilderContext : BuilderContext
    {
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
            : this(new Locator())
        {
        }
public MyBuilderContext(IReadWriteLocator locator)
        {
            InnerLocator = locator;
            SetLocator(InnerLocator);
            StrategyChain = InnerChain;
            SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
        }
    }
public class TestObject
    {
public string _id;
public string ID
        {
get
            {
return _id;
            }
set
            {
                _id = value;
            }
        }
    }
}

在这个例子中,我们建立了两个PropertySetterPolicy对象,分别以ID2、ID2为id加到了context.Policies中,当CreationStrategy建立对象时,它是以下面的程序代码来取得对应的Policy对象。

private object BuildUpNewObject(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
     ICreationPolicy policy = context.Policies.Get<ICreationPolicy>(typeToBuild, idToBuild);
     ………………
}

这段程序代码告诉我们一个重点,ObjectBuidler是以『类型/id』来做类型识别动作,也就是说TestObject+”ID1”、TestObject+”ID2”被ObjectBuilder视为两个不同的对象建立动作,你可以分别为其设定专属的Policy对象,也可以于调用BuildUp方法时,指定不同的id来建立同类型,但不同id的对象。另一个会使用『类型/id』来做识别的是DependencyResolutionLocatorKey对象,我们之前常使用它来完成Injection动作,而SingletonStrategy、DependencyParameter也都是运用它来完成所需完成的工作,其构造函数如下所示。

public DependencyResolutionLocatorKey(Type type, string id)

这意味着,当我们使用SingletonStrategy时,可以利用『类型/id』来建立两个同类型但不同id的Singleton对象,如程序32所示。

程序32

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_SingletonTwoTest
{
class Program
    {
static void Main(string[] args)
        {
            MyBuilderContext context = new MyBuilderContext();
            context.InnerChain.Add(new SingletonStrategy());
            context.InnerChain.Add(new CreationStrategy());
            context.Policies.Set<ISingletonPolicy>(new SingletonPolicy(true), typeof(TestObject), “ID1”);
            context.Policies.Set<ISingletonPolicy>(new SingletonPolicy(true), typeof(TestObject), “ID2”);
            context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
            TestObject obj1 = (TestObject)context.HeadOfChain.BuildUp(context, typeof(TestObject), null, “ID1”);
            TestObject obj2 = (TestObject)context.HeadOfChain.BuildUp(context, typeof(TestObject), null, “ID2”);
if (obj1 == obj2)
                Console.WriteLine(“Singleton”);
            Console.Read();
        }
    }
internal class MyBuilderContext : BuilderContext
    {
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
            : this(new Locator())
        {
        }
public MyBuilderContext(IReadWriteLocator locator)
        {
            InnerLocator = locator;
            SetLocator(InnerLocator);
            StrategyChain = InnerChain;
            SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
        }
    }
public class TestObject
    {
    }
}

这个例子将TestObject+”ID1”、TestObject+”ID2”设定为两个不同的Singleton对象,所以当首次建立并指定id时,所建立出来的两个对象是相异的,也就是说,可以利用『类型/id』来建出两个Singleton系统。

5-5、StrategyList

在本文一开始的范例中,我们使用Builder对象来建立对象,它使用了一个StrategyList对象来处理Strategy串行,这个对象提供了两个重要的方法,一是MakeStrategyChain,它会将StrategyList中的Strategy输出成BuilderStrategyChain对象,这是一个实现了IBuilderStrategyChain接口的对象,也是IBuilderContext所要求的Strategy串行对象。第二个方法是MakeReverseStrategyChain,它会将内含的Strategys反相排序后输出成BuilderStrategyChain对象,这个动作是为了准备TearDown时所需的Strategy串行,还记得前面提过,TearDown的Strategy顺序应该与建立时完全相反,这样才能让对象与其相关的子对象适当的释放。

5-6、TStageEnum

StrategyList是一个泛型对象,它接受一个Enum类型,会依照Enum中所定义的元素来建立Strategy串行或是反相排序,要了解这个设计的原意,我们得先看看ObjectBuilder中所预定义,用来指定给StrategyList的列举。

public enum BuilderStage
{
    PreCreation,
    Creation,
    Initialization,
    PostInitialization
}

读者可以查觉,这与我们先前将Strategy分成四种类型的方式相呼应,StrategyList会依据PreCreation、Creation、Initialization、PostInitialization的顺序来产生BuilderStrategyChain对象,这样就不会因为错置Strategy的顺序,导致程序不正常(例如,先加入CreationStrategy再加入TypeMappingStrategy时,TypeMappingStrategy将无法运作)。Builder对象充份展示了BuilderStage与StrategyList的运用方式。

public Builder(IBuilderConfigurator<BuilderStage> configurator)
{
    Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);
    Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization);
    Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization);
    Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization);
    Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
if (configurator != nulll)
        configurator.ApplyConfiguration(this);
}

只要传入的BuilderStage是正确的,不管TypeMappingStrategy是加在CreationStrategy前面或后面,皆可正常运作。不过同一类型的Strategy,但有顺序需求的情况下,仍然要小心调整顺序,程序32示范了运用BuilderStage所带来的优点。

程序32

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_StrategyListTest
{
class Program
    {
static void Main(string[] args)
        {
            MyBuilder builder = new MyBuilder();
            ITypeMappingPolicy policy = new TypeMappingPolicy(typeof(TestObject), null);
            builder.Policies.Set<ITypeMappingPolicy>(policy, typeof(ITestInterface), null);
            ITestInterface obj1 = builder.BuildUp<ITestInterface>(new Locator(), null, null);
            Console.Read();
        }
    }
public class MyBuilder : BuilderBase<BuilderStage>
    {
public MyBuilder()
            : this(null)
        {
        }
public MyBuilder(IBuilderConfigurator<BuilderStage> configurator)
        {
            Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);
            Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
            Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
            Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
            Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation);
            Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation);
            Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization);
            Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization);
            Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization);
            Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
if (configurator != null)
                configurator.ApplyConfiguration(this);
        }
    }
public interface ITestInterface
    {
    }
public class TestObject : ITestInterface
    {
    }
}

5-6、PolicyList

BuilderContext所定义的Policies对象类型为PolicyList,PolicyList对象以Dictionary对象来储存设计者所加入的Policy对象,其中用来作为键值的BuilderPolicyKey类别构造函数如下。

public BuilderPolicyKey(Type policyType, Type typePolicyAppliesTo, string idPolicyAppliesTo)

第一个参数为policyType,也就是ICrationPolicy、ITypeMappingPolicy等之类,第二个参数是对应的类型,第三个参数则是id。设计者可以调用PolicyList.Set方法来加入一个Policy至内部的存储器中,该方法会依据传入的参数建立BuilderPolicyKey做为键值,然后将Policy加到Dictionary中,如下所示。

public void Set(Type policyInterface, IBuilderPolicy policy, Type typePolicyAppliesTo,
string idPolicyAppliesTo)
{
    BuilderPolicyKey key = new BuilderPolicyKey(policyInterface, typePolicyAppliesTo, idPolicyAppliesTo);
lock (lockObject)
    {
        policies[key] = policy;
    }
}

另一个泛型类型的Set方法也可以达到同样的效果。

public void Set<TPolicyInterface>(TPolicyInterface policy, Type typePolicyAppliesTo,
string idPolicyAppliesTo) where TPolicyInterface : IBuilderPolicy
{
     Set(typeof(TPolicyInterface), policy, typePolicyAppliesTo, idPolicyAppliesTo);
}

设计者可以透过PolicyList.Get方法来取得对应的Policy对象,该方法如下所示。

public TPolicyInterface Get<TPolicyInterface>(Type typePolicyAppliesTo, string idPolicyAppliesTo)
    where TPolicyInterface : IBuilderPolicy
{
return (TPolicyInterface)Get(typeof(TPolicyInterface), typePolicyAppliesTo, idPolicyAppliesTo);
}
public IBuilderPolicy Get(Type policyInterface, Type typePolicyAppliesTo, string idPolicyAppliesTo)
{
    BuilderPolicyKey key = new BuilderPolicyKey(policyInterface,
        typePolicyAppliesTo, idPolicyAppliesTo);
lock (lockObject)
    {
        IBuilderPolicy policy;
if (policies.TryGetValue(key, out policy))
return policy;
        BuilderPolicyKey defaultKey = new BuilderPolicyKey(policyInterface, null, null);
if (policies.TryGetValue(defaultKey, out policy))
return policy;
return null;
    }
}

SetDefault则可以用一个Policy来提供给所有类型使用,Get方法在找不到对应『类型/id』对应的Policy时,就会以该Policy回传。

六、Locator

ObjectBuilder利用Locator对象来实现Service Locator,也利用Locator来进行Dependency Injection,在ObjectBuilder的架构上,Locator有两种类型,一是Readonly Locator,顾名思义,这类Locator只允许读取、不允许新增。二是ReadWriteLocator,它是允许新增、读取类的Locator。我们可以从Visual Studio 2005的Class Diagram来观察ObjectBuilder中的Locator阶层架构。

图7

6-1、Readonly Locator

ObjectBuidler定义了一个IReadableLocator接口,所有的Locator都必须直接或间接实现此接口,内建实现此接口的类别是ReadableLocator,它是一个抽象类。真正完成实现可用的是ReadOnlyLocator,这个Locator只允许读取,不允许新增。

6-2、ReadWrite Locator

ObjectBuilder中支持读与写的Locator是ReadWriterLocator,与ReadOnlyLocator一样,它也是一个抽象类,真正完成实现的是Locator类别。附带一提,虽然Locator定义了蛮清楚的阶层,但是BuilderContext只支持实现IReadWriterLocator接口的Locator。

6-3、WeakRefDictionary and Locator

Locator类别是我们一直都在使用的Locator,它是一个继承自ReadWriterLocator的类别,值得一提的是,它使用一个WeakRefDictionary来储存设计者所放入Locator的对象,WeakRefDictionary内部对于每个元素都会以WeakReference封装,这意味着,Locator中的元素并无法保证一直都存在,因为CLR会在内存拮据时,先行释放WeakRefernce所封装的对象,这点读者必须谨记。

七、Extending ObjectBuilder

ObjectBuilder除了支持三种Dependency Injection模式、Service Locator之外,最大的魅力应该来自于具高度延展性的架构,设计者可以透过撰写Strategy、Policy、Locator等类别来参与对象的建立动作,本章以两个范例来证明这点,一是EventSetterStrategy,它提供Event Injection功能,二是PoolStrategy,提供Pool模式的对象建立。

7-1、EventSetterStrategy

ObjectBuidler提供了Constructor Injection、Interface Injection(Method Ijection)、Setter Injection(Property Injection)三种Injection模式,虽然ObjectBuilder只提供了Propety式的Setter Injection,不过我们可以藉助于ObjectBuilder高度的延展性架构,让ObjectBuidler也能支持Event Injection。

  • IEventSetterInfo

Event Injection与Property Injection同属Setter Injection模式,两者运作的模式也极为相似,ObjectBuilder在Property Injection部份是由ProperySeterInfo、PropertySetterPolicy及PropertySetterStrategy三个类别所构筑而成,我们可以依循这个既定架构,实现Event Injection功能。首要必须定义一个IEventSetterInfo接口,这相对于IPropertySetterInfo接口之于Property Injection。

程序33

public interface IEventSetterInfo
{
object GetValue(IBuilderContext context, Type type, string id, EventInfo propInfo);
     EventInfo SelectEvent(IBuilderContext context, Type type, string id);
}

IEventSetterInfo接口定义了两个方法,SelectEvent方法是用来取得欲Injection事件的EventInfo对象,EventSetterStrategy会调用此方法来取得欲Injection事件的EventInfo对象,然后透过EventInfo.AddHandler来进行注入动作,这个注入动作所使用的值是透过调用IEventSetterInfo.GetValue方法来取得,此接口的实现程序代码如34。

程序34

public sealed class EventSetterInfo : IEventSetterInfo
{
private string _name = null;
private IParameter _value = null;
    #region IEventSetterInfo Members
public object GetValue(IBuilderContext context, Type type, string id, EventInfo propInfo)
    {
return _value.GetValue(context);
    }
public EventInfo SelectEvent(IBuilderContext context, Type type, string id)
    {
return type.GetEvent(_name);
    }
    #endregion
public EventSetterInfo(string name, IParameter value)
    {
        _name = name;
        _value = value;
    }
}

  • IEventSetterPolicy

前面提过,Strategy是与类型无关的设计,因此需要Policy的协助,我们所设计的EventSetterStrategy也是一样,Event Injection必须具备针对不同『类型/id』进行Event Injection的能力,所以必须设计一个IEventSetterPolicy接口,该接口必须直接或间接继承自IBuilderPolicy接口,这是ObjectBuilder对于Policy的规范。

程序35

public interface IEventSetterPolicy : IBuilderPolicy
{
    Dictionary<string, IEventSetterInfo> Events { get;}
}

针对同一『类型/id』对象可能需要注入一个以上的事件,此接口定义了一个Dictionary<string,IEventSetterInfo>对象,让设计者可以指定一个以上的Event Injection动作,36是此接口的实现。

程序36

public sealed class EventSetterPolicy : IEventSetterPolicy
{
private Dictionary<string, IEventSetterInfo> _events = new Dictionary<string, IEventSetterInfo>();
     #region IEventPolicy Members
public Dictionary<string, IEventSetterInfo> Events
     {
get
         {
return _events;
         }
     }
     #endregion
}

  • EventSetterStrategy

完成了基础类别的设计与实现后,剩下的就是Strategy,也就是EventSetterStrategy的设计与实现了,设计上,EventSetterStrategy只有一个任务,就是于BuildUp方法被调用时,透过『类型/id』经由context.Locator取得对应的IEventSetterPolicy对象,再透过它取得欲进行注入动作的IEventSetterInfo对象,接着调用IEventSetterInfo.SelectEvent方法取得EventInfo对象,最后调用IEventSetterInfo.GetValue取得欲注入的Event Handler对象,然后调用EventInfo.AddHandler方法完成注入动作。

程序37

public class EventSetterStrategy : BuilderStrategy
{
public override object BuildUp(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
    {
if (existing != null)
            InjectEvents(context, existing, idToBuild);
return base.BuildUp(context, typeToBuild, existing, idToBuild);
    }
private void InjectEvents(IBuilderContext context, object obj, stringg id)
    {
if (obj == null)
return;
        Type type = obj.GetType();
        IEventSetterPolicy policy = context.Policies.Get<IEventSetterPolicy>(type, id);
if (policy == null)
return;
foreach (IEventSetterInfo eventSetterInfo in policy.Events.Values)
        {
            EventInfo eventInfo = eventSetterInfo.SelectEvent(context, type, id);
if (eventInfo != null)
            {
if (TraceEnabled(context))
                    TraceBuildUp(context, type, id, “Event Setter”, eventInfo.Name);
                eventInfo.AddEventHandler(obj,
                    eventSetterInfo.GetValue(context, type, id, eventInfo) as Delegate);
           }
        }
    }
}

  • Testing

EventSetterStrategy的使用方式与PropertySetterStrategy相似,如38所示。

程序38

using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace EventSetterTest
{
class Program
    {
static void Main(string[] args)
        {
            Builder builder = new Builder();
            builder.Strategies.AddNew<EventSetterStrategy>(BuilderStage.Initialization);
            IEventSetterPolicy policy = new EventSetterPolicy();
            EventHandler handler = new EventHandler(CallHandler);
            policy.Events.Add(“Call”, new EventSetterInfo(“Call”,
new ValueParameter(typeof(EventHandler), handler)));
            builder.Policies.Set<IEventSetterPolicy>(policy, typeof(TestObject), null);
            TestObject obj = builder.BuildUp<TestObject>(new Locator(), null, null);
            obj.RaiseCall();
            Console.ReadLine();
        }
static void CallHandler(object sender, EventArgs args)
        {
            Console.WriteLine(“Called”);
        }
    }
public class TestObject
    {
private EventHandlerList _events = new EventHandlerList();
private static object _onCall = new object();
public event EventHandler Call
        {
            add
            {
                _events.AddHandler(_onCall, value);
            }
            remove
            {
                _events.RemoveHandler(_onCall, value);
            }
        }
protected virtual void OnCall(EventArgs args)
        {
            EventHandler handler = (EventHandler)_events[_onCall];
if (handler != null)
                handler(this, args);
        }
public void RaiseCall()
        {
            OnCall(EventArgs.Empty);
        }
    }
}

图8是此程序的运行结果。

图8

7-2、PoolStrategy

GoF的书中,提出了三种对象管理Pattern,一是Singleton,意味着对象一旦建立后,就存放于某个存储器中,之后所有要求对象的建立动作,都将会获得同样的对象实体,在ObjectBuilder中实现这个Pattern的就是SingletonStrategy。第二个Pattern是SingleCall模式,意味所有的对象建立动作都会获得一个新的对象实体,跟new、create等语言所定义的对象建立模式相同,在Service模式中,SingleCall也意味着Service对象会在要求到达时建立,结束后就立即的释放,这两个模式都可以用ObjectBuilder轻易的实现。第三种Pattern就是Pool,也就是说在特定存储器中维持一定数量的对象实体,当要求对象建立动作时,系统会遍寻存储器中的对象,如果有对象标示为未使用状态,那么系统就回传该对象,并将该对象标示为使用中,本节将实现一个PoolStrategy,让ObjectBuilder可以具备Pool的能力。

  • PoolFactory

Pool Pattern的核心就是一个可以于存储器中管理对象的能力,此处使用笔者书中所设计的PoolFactory类别来完成这个目的。

程序39

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace Orphean.WinFormHelper.Framework.Factorys
{
///<summary>
/// a interface to be implement by Object Factory,
/// DAL use object factory to speed object constructing.
///</summary>
public interface IObjectFactory
    {
///<summary>
/// acquire a object.
///</summary>
///<param name=”type”>object Type</param>
///<returns>object</returns>
object AcquireObject(Type type);
///<summary>
/// release a object.
///</summary>
///<param name=”obj”>a object to releasing</param>
void ReleaseObject(object obj);
    }
public sealed class PoolObjectFactory : IObjectFactory, IDisposable
    {
class PoolData
        {
public bool InUse = falsee;
public object obj;
        }
private IList _storage;
private int _max = 100;
private bool _limit = false;
private IBuilderContext _context = null;
public PoolObjectFactory(IBuilderContext context, int max, bool limit, IList storage)
            : this(context)
        {
            _max = max;
            _limit = limit;
            _storage = storage;
        }
public PoolObjectFactory(IBuilderContext context)
        {
            _context = context;
        }
private PoolData GetPoolData(object obj)
        {
lock (_storage.SyncRoot)
            {
for (int i = 0; i < _storage.Count; i++)
                {
                    PoolData p = (PoolData)_storage[i];
if (p.obj == obj)
return p;
                }
            }
return null;
        }
private object GetObject(Type type)
        {
lock (_storage.SyncRoot)
            {
if (_storage.Count > 0)
                {
if (((PoolData)_storage[0]).obj.GetType() != type)
throw new Exception(
string.Format(“the Pool Factory only for Type :{0}”, _storage[0].GetType().Name));
                }
for (int i = 0; i < _storage.Count; i++)
                {
                    PoolData p = (PoolData)_storage[i];
if (!p.InUse)
                    {
                        p.InUse = true;
return p.obj;
                    }
                }
if (_storage.Count > _max && _limit)
throw new Exception(“max limit is arrived.”);
object obj = _context.HeadOfChain.BuildUp(_context, type, null, null);
                PoolData p1 = new PoolData();
                p1.InUse = true;
                p1.obj = obj;
                _storage.Add(p1);
return obj;
            }
        }
private void PutObject(object obj)
        {
            PoolData p = GetPoolData(obj);
if (p != null)
                p.InUse = false;
        }
        #region IObjectFactory Members
public object AcquireObject(Type type)
        {
return GetObject(type);
        }
public void ReleaseObject(object obj)
        {
if (_storage.Count > _max)
            {
if (obj is IDisposable)
                    ((IDisposable)obj).Dispose();
                PoolData p = GetPoolData(obj);
lock (_storage.SyncRoot)
                    _storage.Remove(p);
return;
            }
            PutObject(obj);
        }
        #endregion
        #region IDisposable Members
public void Dispose()
        {
lock (_storage.SyncRoot)
            {
for (int i = 0; i < _storage.Count; i++)
                {
                    PoolData p = (PoolData)_storage[i];
if (p.obj is IDisposable)
                        ((IDisposable)p.obj).Dispose();
                }
            }
        }
        #endregion
    }
}

本文的重点在于ObjectBuilder的应用与延伸,所以此处就不在赘述PoolFactory的实现细节。

  • IPoolPolicy

PoolStrategy在架构上与SingletonStrategy类似,我们必须设计一个IPoolPolicy接口,该接口的定义如程序40。

程序40

public interface IPoolPolicy : IBuilderPolicy
{
bool IsPool { get;}
}

此接口只定义了一个Pool属性,用来告诉PoolStrategy那个『类型/id』是需要Pool,那个又是不需要的,虽然设计者可以针对要Pool的『类型/id』来指定IPoolPolicy,如果有特定对象不需要Pool动作,那就不指定IPoolPocy即可,但是我们无法排除一种情况,那就是系统里大多数对象都需要Pool,仅有特定的对象不需要Pool,此时要特别对一个个对象设定IPoolPolicy的话,会相当的繁琐。此时设计者可以以SetDefault来加入IPoolPolicy对象,将所有对象标示为可Pool,再针对不需要Pool的对象来指定IPoolPolicy。程序41是实现此接口的程序代码列表。

程序41

public class PoolPolicy : IPoolPolicy
{
private bool _isPool = false;
    #region IPoolPolicy Members
public bool IsPool
    {
get
        {
return _isPool;
        }
    }
    #endregion
public PoolPolicy(bool isPool)
    {
        _isPool = isPool;
    }
}

  • PoolStrategy

PoolStrategy必须在BuildUp方法运用PoolFactory来取得要求的对象,在设计上,我们会为每个『类型/id』建立独立的PoolFactory对象,这意味着每个『类型/id』的对象数量是独立管理的。

程序42

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
using Orphean.WinFormHelper.Framework.Factorys;
namespace OB_PoolStrategy
{
public class PoolStrategy : BuilderStrategy
    {
private WeakRefDictionary<object, object> _factoryMap =
new WeakRefDictionary<object, object>();
private bool _poolObjectCreating = false;
public override object BuildUp(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
        {
if (!_poolObjectCreating)
            {
                IPoolPolicy policy = context.Policies.Get<IPoolPolicy>(typeToBuild, idToBuild);
if (policy != null && policy.IsPool)
                {
                    PoolLocatorKey key = new PoolLocatorKey(typeToBuild, idToBuild);
                    PoolObjectFactory factory = null;
if (context.Locator.Contains(key))
                    {
                        factory = context.Locator.Get<PoolObjectFactory>(key);
lock (this)
                        {
                            _poolObjectCreating = true;
try
                            {
                                existing = factory.AcquireObject(typeToBuild);
                            }
finally
                            {
                                _poolObjectCreating = false;
                            }
                        }
                    }
else
                    {
                        factory = new PoolObjectFactory(context, 15, false, new ArrayList());
                        _poolObjectCreating = true;
try
                        {
                            existing = factory.AcquireObject(typeToBuild);
                        }
finally
                        {
                            _poolObjectCreating = false;
                        }
                        context.Locator.Add(key, factory);
                    }
if (!_factoryMap.ContainsKey(existing))
                        _factoryMap.Add(existing, factory);
                }
            }
return base.BuildUp(context, typeToBuild, existing, idToBuild);
        }
public override object TearDown(IBuilderContext context, object item)
        {
if (_factoryMap.ContainsKey(item))
            {
                PoolObjectFactory factory = _factoryMap[item] as PoolObjectFactory;
if (factory != null)
                    factory.ReleaseObject(item);
                _factoryMap.Remove(item);
            }
return base.TearDown(context, item);
        }
    }
public sealed class PoolLocatorKey
    {
private Type type;
private string id;
public PoolLocatorKey()
            : this(null, null)
        {
        }
public PoolLocatorKey(Type type, string id)
        {
this.type = type;
this.id = id;
        }
public string ID
        {
get { return id; }
        }
public Type Type
        {
get { return type; }
        }
public override bool Equals(object obj)
        {
            PoolLocatorKey other = obj as PoolLocatorKey;
if (other == null)
return false;
return (Equals(type, other.type) && Equals(id, other.id));
        }
public override int GetHashCode()
        {
int hashForType = type == null ? 0 : type.GetHashCode();
int hashForID = id == null ? 0 : id.GetHashCode();
return hashForType ^ hashForID;
        }
    }
}

在BuildUp方法被调用时,PoolStrategy会透过context.Policies取得『类型/id』对应的IPoolPolicy对象,判断此次建立动作是否使用Pool,是的话就以『类型/id』至Locator中取出PoolFactory,如果Locator已经有该PoolFactory时,就直接调用PoolFactory.AcquireObject方法来取得对象实体,如果Locator中无对应的PoolFactory时,就建立一个并放入Locator中。在这个建立流程中有几个重点,第一!我们将PoolFactory储存在Locator中,因此需要一个类似DependencyResolutionLocatorKey的对象,用来做为由Locator取出PoolFactory的键值,这个对象必须覆载Equal、GetHashCode两个方法,因为Locator会调用这两个方法来比对键值,这个对象就是PoolLocatorKey。第二!PoolFactory在存储器中没有可使用对象时,会调用BuilderContext.HeadChain.BuildUp方法来建立该对象,这会引发重进入的问题,BuilderContext.HeadChain.BuildUp方法将会再次触发PoolStrategy的BuildUp,而这里又会再次调用BuilderContext.HeadChain.BuildUp,造成重入的问题,所以此处利用一个旗标:poolObjectCreating来解决这个问题。第三!PoolStrategy必须在TearDown方法被调用时,调用PoolFactory.ReleaseObject来将该对象归还,此时会遭遇到一个问题,因为TearDown方法只会传入对象实体,没有id的信息,这使得PoolStrategy无法于此处取得对应的PoolFactory对象,为了解决此问题,PoolStrategy宣告了一个_factoryMap对象,它是一个WeakRefDictionary<object, object>类别对象,在对象实体于BuildUp方法被建立后,PoolStrategy会将object/PoolFactory成对放入_factoryMap中,这样就能于TearDown时以对象实体至_factoryMap中取出对应的PoolFactory对象了。

  • Testing

PoolStrategy的使用方式与SingletonStrategy类似,程序43是应用的程序代码列表。

程序43

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_PoolStrategy
{
class Program
    {
static void Main(string[] args)
        {
            Builder builder = new Builder();
            builder.Strategies.AddNew<PoolStrategy>(BuilderStage.PreCreation);
            IPoolPolicy policy = new PoolPolicy(true);
            builder.Policies.Set<IPoolPolicy>(policy, typeof(TestObject), null);
            Locator locator = new Locator();
            TestObject obj1 = builder.BuildUp<TestObject>(locator, null, null);
            TestObject obj2 = builder.BuildUp<TestObject>(locator, null, null);
            builder.TearDown<TestObject>(locator, obj1);
            builder.TearDown<TestObject>(locator, obj2);
            TestObject obj3 = builder.BuildUp<TestObject>(locator, null, null);
if (obj3 == obj1 || obj3 == obj2)
                Console.WriteLine(“Pooled”);
            Console.ReadLine();
        }
    }
public class TestObject
    {
    }
}

图9是执行结果。

图9

 

[转][黄忠成]Object Builder Application Block (2)

Filed under: C#,dotNet,IoC,Object Builder — systembug @ 3:13 上午

[转]Object Builder Application Block

文/黄忠成 ;2006/9/21

原文链接:http://blog.csdn.net/Code6421/archive/2006/09/25/1282150.aspx

整理:吕震宇

三、ObjectBuilder Application Block

ObjectBuilder一开始出现于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提供以下的功能。

  • 允许要求一个抽象对象或接口,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。

3-1、The Architecture of Object Builder

图2

图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所示。

图3

值得一提的是,Policy是以Type/id方式,也就是『类型/id』方式来存放,这种做法不只可以让不同类型拥有各自的Policy,也允许同类型但不同id拥有各自的Policy。ObjectBuilder中的最后一个元素是Locator,Locator对象在ObjectBuidler中扮演着前述的Service Locator角色,设计师可以用Key/Value的方式,将对象推入Locator中,稍后再以Key值来取出使用。

3-2、Strategys

ObjectBuilder内建了许多Strategy,这些Strategy可以大略分成四种类型,如图4。

图4

  • Pre-Creation Strategy

Pre-Creation意指对象被建立前的初始动作,参与此阶段的Strategy有:TypeMappingStrategy、PropertyReflectionStrategy、ConstructorReflectionStrategy、MethodReflectionStrategy及SingletonStrategy,稍后我们会一一检视 它们。

  • Creation Strategy

Creation类型的Strategy主要工作在于建立对象,它会利用Pre-Creation Strategys所准备的参数来建立对象,ObjectBuilder就是运用Pre-Creation的ConstructorReflectionStrategy及CreationStrategy来完成Constructor Injection动作。

  • Initialization Strategy

当对象建立后,会进入初始化阶段,这就是Initialization Strategy阶段,在此阶段中,PropertySetterStrategy会与PropertyReflectionStrategy合作,完成Setter Injection。而MethodExecutionStrategy则会与MethodReflectionStrategy合作,在对象建立后,调用特定的方法,也就是Method Injection(视使用方式,Interface Injection是以此种方式完成的)。

  • Post-Initialization Strategy

在对象建立并完成初始化动作后,就进入了Post-Initialization Strategy阶段,在此阶段中,BuilderAwareStrategy会探询已建立的对象是否实现了IBuilderAware接口,是的话就调用IBuilderAware.OnBuildUp方法。

  • 关于对象释放

先前曾经提过,ObjectBuidler在建立对象时,会一一调用所有Strategy来建立对象,同样的!当释放对象时,ObjectBuilder也会进行同样的动作,不过方向是相反的,在内建的Strategy中,只有BuilderAwareStrategy会参与对象释放的动作,在对象释放时,BuilderAwareStrategy会探询欲释放的对象是否实现了IBuidlerAware接口,是的话就调用IBuidlerAware.OnTearDown方法。

3-3、A Simple Application

再怎么详细的说明,少了一个实例就很难让人理解,本节以一个简单的ObjectBuidler应用实例开始,一步步带领读者进入ObjectBuilder的世界。

程序10

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace SimpleApp
{
class Program
    {
static void Main(string[] args)
        {
            Builder builder = new Builder();
            TestObject obj = builder.BuildUp<TestObject>(new Locator(), null, null);
            obj.SayHello();
            Console.ReadLine();
        }
    }
public class TestObject
    {
public void SayHello()
        {
            Console.WriteLine(“TEST”);
        }
    }
}

这是一个相当阳春的例子,在程序一开始时建立了一个Builder对象,它是ObjectBuilder所提供的Facade对象,其会预先建立一般常用的Strategy串行,并于BuilderUp方法被调用时,建立一个BuilderContext对象,并将Srategy串行及Polices串行指定给该BuilderContext,然后进行对象的建立工作。

  • How Object Creating

要了解前面的例子中,TestObject对象究竟是如何被建立起来的,首先必须深入Builder对象的建构动作。

private StrategyList<TStageEnum> strategies = new StrategyList<TStageEnum>();
public BuilderBase()
{
}
public PolicyList Policies
{
get { return policies; }
}
public StrategyList<TStageEnum> Strategies
{
get { return strategies; }
}
public Builder(IBuilderConfigurator<BuilderStage> configurator)
{
    Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation);
    Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);
    Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization);
    Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization);
    Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization);
    Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
if (configurator != null)
        configurator.ApplyConfiguration(this);
}

当Buidler对象被建立时,其构造函数会将前面所提及的几个Strategys加到Strategies这个StrategyList Collection对象中,待BuildUp方法被调用时指定给新建立的BuilderContext对象。

public TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator,
string idToBuild, object existing, params PolicyList[] transientPolicies)
{
return (TTypeToBuild)BuildUp(locator, typeof(TTypeToBuild), idToBuild, 
        existing, transientPolicies);
}
public virtual object BuildUp(IReadWriteLocator locator, Type typeToBuild,
string idToBuild, object existing, params PolicyList[] transientPolicies)
{
………………..
return DoBuildUp(locator, typeToBuild, idToBuild, existing, transientPolicies);
……………….
}
private object DoBuildUp(IReadWriteLocator locator, Type typeToBuild, 
string idToBuild, object existing, PolicyList[] transientPolicies)
{
    IBuilderStrategyChain chain = strategies.MakeStrategyChain();
………………..
    IBuilderContext context = MakeContext(chain, locator, transientPolicies);
………………..                           
object result = chain.Head.BuildUp(context, typeToBuild, existing, idToBuild);
………………..
}
private IBuilderContext MakeContext(IBuilderStrategyChain chain,
        IReadWriteLocator locator, params PolicyList[] transientPolicies)
{
………………..
return new BuilderContext(chain, locator, policies);
}

当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等对象的机会。

  • Policy物件的用途

现在,我们弄清楚了Strategy的用途,BuilderContext的真正涵意,但还有两个元素尚未厘清,其中之一就是Policy对象,前面曾经稍微提过,Strategy是与类型无关的设计概念,因此为了针对不同类型做个别的处理,我们需要另一个与类型相关的设计,那就是Policy对象,要确认这点,必须重返Builder的构造函数。

public Builder(IBuilderConfigurator<BuilderStage> configurator)
{
………………
    Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
……………..
}

这里调用了Policies的SetDefault方法,Policies是一个PolicyList对象,其提供了推入(Set、SetDefault)及取出(Get)方法,允许设计者针对所有『类型/id』及特定『类型/id』指定对应的IBuilderPolicy对象,那这有什么用呢?这个问题可以由CreationStrategy类别中的以下这段程序代码来回答。

public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
if (existing != null)
        BuildUpExistingObject(context, typeToBuild, existing, idToBuild);
else
        existing = BuildUpNewObject(context, typeToBuild, existing, idToBuild);
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)]
private object BuildUpNewObject(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
{
ICreationPolicy policy = context.Policies.Get<ICreationPolicy>(typeToBuild, idToBuild);
…………………….
    InitializeObject(context, existing, idToBuild, policy);
return existing;
}
private void InitializeObject(IBuilderContext context, object existing, string id, ICreationPolicy policy)
{
…………………….
    ConstructorInfo constructor = policy.SelectConstructor(context, type, id);
…………………….
object[] parms = policy.GetParameters(context, type, id, constructor);
…………………….             
    method.Invoke(existing, parms);
}

如你所见,CreationStrategy于建立对象时,会由Policies中取出『类型/id』对应的ICreationPolicy对象,接着利用 它来取得ConstructorInfo(构造函数方法),再以GetParameters方法来取得构造函数所需的参数,最后调用此构造函数。这段程序代码告诉我们Policy的真正用途,就是用来协助Strategy于不同『类型/id』对象建立时,采取不同的动作,这也就是说,Strategy与Policy通常是成对出现的。

  • Locator

最后一个尚未弄清楚的关键元素是Locator,我们于调用Builder的BuildUp方法时,建立了一个Locator对象并传入该方法,这是用来做什么的呢?在ObjectBuilder中,Locator扮演两种角色,第一个角色是提供一个对象容器供Strategy使用,这点可以透过以下程序了解。

public class SingletonStrategy : BuilderStrategy
{
public override object BuildUp(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
    {
        DependencyResolutionLocatorKey key = new DependencyResolutionLocatorKey(typeToBuild, idToBuild);
if (context.Locator != null && context.Locator.Contains(key, SearchMode.Local))
        {
            TraceBuildUp(context, typeToBuild, idToBuild, “”);
return context.Locator.Get(key);
        }
return base.BuildUp(context, typeToBuild, existing, idToBuild);
    }
}

SingletonStrategy是一个用来维持某一个对象只能有一份实体存在,当此Strategy被唤起时,其会先至Locator寻找目前要求的对象是否已被建立,是的话就取出该对象并传回。Locator同时也可以作为一个Service Locator,这点可以由以下程序代码来验证。

locator.Add(“Test”,new TestObject());
………….
TestObject obj = locator.Get<TestObject>(“Test”);

当然,这种手法有一个问题,那就是TestObject对象是预先建立后放在Locator中,这并不是一个好的设计,后面的章节我们会提出将Service Locator与Dependency Injection整合的手法。

PS:ObjectBuidler的Locator离完善的Service Locator还有段距离。

四、Dependency Injection With ObjectBuilder

ObjectBuilder支持Dependency Injection中定义的三种Injection模式,本章将一一介绍如何运用ObjectBuilder来实现。

4-1、Constructor Injection

Constructor Injection的精神在于使用构造函数来进行注入动作,本节延用InputAccept的例子,程序11是改采ObjectBuilder进行Constructor Injection的例子。

程序11

using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using Microsoft.Practices.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
namespace OB_ConstructorInjectionTest
{
class Program
    {
static void UseValueParameter(MyBuilderContext context)
        {
            ConstructorPolicy creationPolicy = new ConstructorPolicy();
            creationPolicy.AddParameter(new ValueParameter(typeof(IDataProcessor), new PromptDataProcessor()));
            context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
        }
static void Main(string[] args)
        {
            MyBuilderContext context = new MyBuilderContext(new Locator());
            context.InnerChain.Add(new CreationStrategy());
            UseValueParameter(context);
            InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context, typeof(InputAccept), null, null);
            accept.Execute();
            Console.Read();
        }

    }
internal class MyBuilderContext : BuilderContext
    {
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
            : this(new Locator())
        {
        }
public MyBuilderContext(IReadWriteLocator locator)
        {
            InnerLocator = locator;
            SetLocator(InnerLocator);
            StrategyChain = InnerChain;
            SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
                Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
        }
    }
public class InputAccept
    {
private IDataProcessor _dataProcessor;
public void Execute()
        {
            Console.Write(“Please Input some words:”);
string input = Console.ReadLine();
            input = _dataProcessor.ProcessData(input);
            Console.WriteLine(input);
        }
public InputAccept(IDataProcessor dataProcessor)
        {
            _dataProcessor = dataProcessor;
        }

    }
public interface IDataProcessor
    {
string ProcessData(string input);
    }
public class DummyDataProcessor : IDataProcessor
    {
        #region IDataProcessor Members
public string ProcessData(string input)
        {
return input;
        }
        #endregion
    }
public class PromptDataProcessor : IDataProcessor
    {
        #region IDataProcessor Members
public string ProcessData(string input)
        {
return “your input is: ” + input;
        }
        #endregion
    }
}

程序于一开始时,建立了一个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是这整个流程的示意图。

图5

图中读者可能会有所迷惑的是,FormatterServices.GetSafeUninitializedObject方法是何作用?这是.NET Framework中一个建立对象的途径,与一般new或是Activator.CreateInstance方式不同,GetSafeUninitializedObject方法并不会触发该对象的构造函数,只是单纯的将对象建立起来而已,因此CreationStrategy才必须于最后调用对应的构造函数。

  • Understanding Parameter

程序11中使用了一个ValueParameter对象,要知道这个对象的作用,我们得先了解Parameter在ObjectBuilder中所代表的意义,在三种注入模式中,有一个共通的规则,就是需要有参数来注入,Constructor Injection是透过构造函数参数注入,而Interface Injection则是透过函数参数注入,Setter Injection则是透过属性注入,因此参数是这三种注入模式都会用到的观念,所以ObjectBuilder定义了IParameter接口,并提供一组实现此接口的参数对象,于注入时期由这些参数对象来取得参数值,如图6。

图6

  • ValueParameter

这是一个最简单的Paramter对象,构造函数如下所示:

public ValueParameter(Type valueType, object value)

它的GetValue方法仅是将构造函数传入的value对象传回而已。

  • DependencyParamter

DependencyParameter是一个功能强大的Parameter对象,程序12是以DependencyParameter来取代ValueParameter完成Constructor Injection的例子。

程序12

static void UseDependencyParameter(MyBuilderContext context)
{
   ConstructorPolicy creationPolicy = new ConstructorPolicy();
   creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor),null,
typeof(PromptDataProcessor),NotPresentBehavior.CreateNew,SearchMode.Local));
   context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
   ConstructorPolicy creationPolicy2 = new ConstructorPolicy();
   context.Policies.Set<ICreationPolicy>(creationPolicy2, typeof(PromptDataProcessor),null);
}

读者可以发现,DependencyParameter并未要求建构者传入任何对象实体,而是要求建构者传入注入时对应的参数类型、参数名称、实体类型、NotPersentBehavoir及SearchMode等参数,下面的程序行表是DependencyParameter的构造函数:

public DependencyParameter(Type parameterType, string name,
          Type createType, NotPresentBehavior notPresentBehavior, SearchMode searchMode)

第一个参数是参数的类型,第二个参数是参数的名称,当ConstructorPolicy于SelectConstructor方法时,会依据这两个参数来选取适合的构造函数,第三个参数是实体对象的类型,以本例来说,就是以PromptDataProcessor这个类型建立对象来传入需要IDataProcessor类型的构造函数、方法或属性,第四个参数则影响了DependencyParameter的取值动作,预设情况下,DependencyParameter会先至Locator中取值,这个动作会受到第五个参数:SearchMode的影响(稍后会介绍这一部份),如果找不到的话,就会依据此参数值来做动作,NotPersentBehavior这个列举的定义如下:

public enum NotPresentBehavior
{                   
   CreateNew,
   ReturnNull,
   Throw,
}

CreateNew代表着当DependencyParameter于Locator找不到需要的值时,调用BuilderContext.HeadOfChain.BuildUp方法来建立该对象,以此例来说即是如此,所建立对象

 

[转][黄忠成]Object Builder Application Block (1)

Filed under: C#,dotNet,IoC,Object Builder — systembug @ 3:02 上午

本文相关代码下载

[转]Object Builder Application Block

文/黄忠成 ;2006/9/21

原文链接:http://blog.csdn.net/Code6421/archive/2006/09/25/1282139.aspx

整理:吕震宇

一、IoC 简介

IoC的全名是『Inversion of Control』,字面上的意思是『控制反转』,要了解这个名词的真正含意,得从『控制』这个词切入。一般来说,当设计师撰写一个Console程序时,控制权是在该程序上,它决定着何时该印出讯息、何时又该接受使用者输入、何时该进行数据处理,如程序1。

程序1

using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
    {
static void Main(string[] args)
        {
            Console.Write(“Please Input Some Words:”);
string inputData = Console.ReadLine();
            Console.WriteLine(inputData);
            Console.Read();
        }
    }
}

从整个流程上看来,OS将控制权交给了此程序,接下来就看此程序何时将控制权交回,这是Console模式的标准处理流程。程序1演译了『控制』这个字的意思,那么『反转』这个词的含义呢?这可以用一个Windows Application来演示,如程序2。

程序2

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsApplication10
{
public partial class Form1 : Form
    {
public Form1()
        {
            InitializeComponent();
        }
private void button1_Click(object sender, EventArgs e)
        {
            MessageBox.Show(textBox1.Text);
        }
    }
}

与程序1不同,当程序2被执行后,控制权其实并不在此程序中,而是在底层的Windows Forms Framework上,当此程序执行后,控制权会在Application.Run函数调用后,由主程序转移到Windows Forms Framework上,进入等待讯息的状态,当用户按下了Form上的按钮后,底层的Windows Forms Framework会收到一个讯息,接着会依照讯息来 调用button1_Click方法,此时控制权就由Windows Forms Framework转移到了主程序。程序2充份演译了『控制反转』的意含,也就是将原本位于主程序中的控制权,反转到了Windows Forms Framework上。

二、Dependency Injection

IoC的中心思想在于控制权的反转,这个概念于现今的Framework中相当常见,.NET Framework中就有许多这样的例子,问题是!既然这个概念已经 实现于许多Framework中,那为何近年来IoC会于社群引起这么多的讨论?著名的IoC实现对象如Avalon、Spring又达到了什么目的呢?就笔者的认知,IoC是一个广泛的概念,主要中心思想就在于控制权的反转,Windows Forms Framework与Spring在IoC的大概念下,都可以算是IoC的实现对象,两者不同之处在于究竟反转了那一部份的控制权,Windows Forms Framework将主程序的控制权反转到了自身上,Spring则是将对象的建立、释放、配置等控制权反转到自身,虽然两者都符合IoC的大概念,但设计初衷及欲达成的目的完全不同,因此用IoC来统称两者,就显得有些笼统及模糊。设计大师Martin Fowler针对Spring这类型IoC实现对象提出了一个新的名词『Dependency Injection』,字面上的意思是『依赖注入』。对笔者而言,这个名词比起IoC更能描述现今许多宣称支持IoC的Framework内部的行为,在Martin Fowler的解释中, Dependency Injection分成三种,一是Interface Injection(接口注射)、Constructor Injection(构造函数注射)、Setter Injection(设值注射)。

2-1、Why we need Dependency Injection?

OK,花了许多篇幅在解释IoC与Dependency Injection两个概念,希望读者们已经明白这两个名词的涵意,在切入Dependency Injection这个主题前,我们要先谈谈为何要使用Dependency Injection,及这样做带来了什么好处,先从程序3的例子开始。

程序3

using System;
using System.Collections.Generic;
using System.Text;
namespace DISimple
{
class Program
    {
static void Main(string[] args)
        {
            InputAccept accept = new InputAccept(new PromptDataProcessor());
            accept.Execute();
            Console.ReadLine();
        }
    }
public class InputAccept
    {
private IDataProcessor _dataProcessor;
public void Execute()
        {
            Console.Write(“Please Input some words:”);
string input = Console.ReadLine();
            input = _dataProcessor.ProcessData(input);
            Console.WriteLine(input);
        }
public InputAccept(IDataProcessor dataProcessor)
        {
            _dataProcessor = dataProcessor;
        }
    }
public interface IDataProcessor
    {
string ProcessData(string input);
    }
public class DummyDataProcessor : IDataProcessor
    {
        #region IDataProcessor Members
public string ProcessData(string input)
        {
return input;
        }
        #endregion
    }
public class PromptDataProcessor : IDataProcessor
    {
        #region IDataProcessor Members
public string ProcessData(string input)
        {
return “your input is: ” + input;
        }
        #endregion
    }
}

这是一个简单且无用的例子,但却可以告诉我们为何要使用Dependency Injection,在这个例子中,必须在建立InputAccept对象时传入一 个实现IDataProcessor接口的对象,这是Interface Base Programming概念的设计模式,这样做的目的是为了降低InputAccept与实现对象间的耦合关系,重用InputAccept的执行流程,以此来增加程序的延展性。那这个设计有何不当之处呢?没有!问题不在InputAccept、IDataProcessor的设计,而在于使用的方式。

InputAccept accept = new InputAccept(new PromptDataProcessor());

使用InputAccept时,必须在建立对象时传入一个实现IDataProcess接口的对象,此处直接建立一个PromptDataProcessor对象传入,这使得主程序与PromptDataProcessor对象产生了关联性,间接的摧毁使用IDataProcessor时所带来的低耦合性,那要如何解决这个问题呢?读过Design Patterns的读者会提出以Builder、Factory等样式解决这个问题,如下所示。

//Factory
InputAccept accept = new InputAccept(DataProcessorFactory.Create());
//Builder
InputAccept accept = new InputAccept(DataProcessorBulder.Build());

两者的实际流程大致相同,DataProcessorFactory.Create方法会依据组态档的设定来建立指定的IDataProcessor实现对象,回传后指定给InputAccept,DataProcessBuilder.Build方法所做的事也大致相同。这样的设计是将原本位于主程序中IDataProcessor对象的建立动作,转移到DataProcessorFactory、DataProcessorBuilder上,这也算是一种IoC观念的实现,只是这种转移同时也将主程序与IDataProcessor对象间的关联,平移成主程序与DataProcessorFactory间的关联,当需要建立的对象一多时,问题又将回到原点,程序中一定会充斥着AFactory、BFactory等Factory对象。彻底将关联性降到最低的方法很简单,就是设计Factory的Factory、或是Builder的Builder,如下所示。

//declare
public class DataProcessorFactory : IFactory
……….
//Builder
public class DataProcessorBuilder : IBuilder
………..
………………..
//initialize
//Factory 
GenericFactory.RegisterTypeFactory(typeof(IDataProcessor),typeof(DataProcessorFactory));
//Builder
GenericFactory.RegisterTypeBuilder(typeof(IDataProcessor),typeof(DataProcessorBuilder));
…………….
//Factory
InputAccept accept = new InputAccept(GenericFactory.Create(typeof(IDataProcessor));
//Builder
InputAccept accept = new InputAccept(GenericBuilder.Build(typeof(IDataProcessor));

这个例子中,利用了一个GenericFactory对象来建立InputAccept所需的IDataProcessor对象,当GenericFactory.Create方法被 调用时,它会查询所拥有的Factory对象对应表,这个对应表是以type of base class/type of factory成对的格式存放,程序必须在一启动时准备好这个对应表,这可以透过组态档或是程序代码来完成,GenericFactory.Create方法在找到所传入的type of base class所对应的type of factory后,就建立该Factory的实体,然后调用该Factory对象的Create方法来建立IDataProcessor对象实体后回传。另外,为了统一Factory的 调用方式,GenericFactory要求所有注册的Factory对象必须实现IFactory接口,此接口只有一个需要实现的方法:Create。方便读者易于理解这个设计概念,图1以流程图呈现这个设计的。

图1

那这样的设计有何优势?很明显的,这个设计已经将主程序与DataProcessorFactory关联切除,转移成主程序与GenericFactory的关联,由于只使用一个Factory:GenericFactory,所以不存在于AFactory、BFactory这类问题。这样的设计概念确实降低了对象间的关联性,但仍然不够完善,因为有时对象的构造函数会需要一个以上的参数,但GenericFactory却未提供途径来传入这些参数(想象当InputAccept也是经由GenericFactory建立时),当然!我们可以运用object[]、params等途径来传入这些参数,只是这么做的后果是,主程序会与实体对象的构造函数产生关联,也就是间接的与实体对象产生关联。要切断这层关联,我们可以让GenericFactory自动完成InputAccept与IDataProcessor实体对象间的关联,也就是说在GenericFactory中,依据InputAccept的构造 函数声明,取得参数类型,然后使用该参数类型(此例就是IDataProcessor)来调用GenericFactory.Create方法建立实体的对象,再将这个对象传给InputAccept的构造函数,这样主程序就不会与InputAccept的构造函数产生关联,这就是Constructor Injection(构造函数注入)的概念。以上的讨论,我们可以理出几个重点,一、Dependency Injection是用来降低主程序与对象间的关联,二、Dependency Injection同时也能降低对象间的互联性,三、Dependency Injection可以简化对象的建立动作,进而让对象更容易使用,试想!只要调用GenericFactory.Create(typeof(InputAccept))跟原先的设计,那个更容易使用?不过要拥有这些优点,我们得先拥有着一个完善的架构,这就是ObjectBuilder、Spring、Avalon等Framework出现的原因。

PS:这一小节进度超前许多,接下来将回归Dependency Injection的三种模式,请注意!接下来几小节的讨论是依据三种模式的精神,所以例子以简单易懂为主,不考虑本文所提及的完整架构。

2-2、Interface Injection

Interface Injection指的是将原本建构于对象间的依赖关系,转移到一个接口上,程序4是一个简单的例子。

程序4

using System;
using System.Collections.Generic;
using System.Text;
namespace ConsoleApplication2
{
class Program
    {
static void Main(string[] args)
        {
            InputAccept accept = new InputAccept();
            accept.Inject(new DummyDataProcessor());
            accept.Execute();
            Console.Read();
        }
    }
public class InputAccept
    {
private IDataProcessor _dataProcessor;
public void Inject(IDataProcessor dataProcessor)
        {
            _dataProcessor = dataProcessor;
        }
public void Execute()
        {
            Console.Write(“Please Input some words:”);
string input = Console.ReadLine();
            input = _dataProcessor.ProcessData(input);
            Console.WriteLine(input);
        }
    }
public interface IDataProcessor
    {
string ProcessData(string input);
    }
public class DummyDataProcessor : IDataProcessor
    {
        #region IDataProcessor Members
public string ProcessData(string input)
        {
return input;
        }
        #endregion
    }
public class PromptDataProcessor : IDataProcessor
    {
        #region IDataProcessor Members
public string ProcessData(string input)
        {
return “your input is: ” + input;
        }
        #endregion
    }
}

InputAccept对象将一部份的动作转移到另一个对象上,虽说如此,但InputAccept与该对象并未建立依赖关系,而是将依赖关系建立在一个接口:IDataProcessor上,经由一个方法传入实体对象,我们将这种应用称为Interface Injection。当然,如你所见,程序4的手法在实务应用上并未带来太多的好处,原因是执行Interface Injection动作的仍然是主程序,这意味着与主程序与该对象间的依赖关系仍然存在,要将Interface Injection的概念发挥到极致的方式有两个,一是使用组态文件,让主程序由组态文件中读入DummaryDataProcessor或是PromptDataProcessor,这样一来,主程序便可以在不重新编译的情况下,改变InputAccept对象的行为。二是使用Container(容器),Avalon是一个标准的范例。

程序5

public class InputAccept implements Serviceable {
private IDataProcessor m_dataProcessor;
public void service(ServiceManager sm) throws ServiceException {
      m_dataProcessor = (IDataProcessor) sm.lookup(“DataProcessor”);
 }
public void Execute() {
    ……..
string input = m_dataProcessor.ProcessData(input);
    ……..
 }
}

在Avalon的模式中,ServiceManager扮演着一个容器,设计者可以透过程序或组态文件,将特定的对象,如DummyDataProcessor推到容器中,接下来InputAccept就只需要询问容器来取得对象即可,在这种模式下,InputAccept不需再撰写Inject方法,主程序也可以藉由ServiceManager,解开与DummyDataProcessor的依赖关系。使用Container时有一个特质,就是Injection动作是由Conatiner来自动完成的,这是Dependency Injection的重点之一。

PS:在正确的Interface Injection定义中,组装InputAccept与IDataProcessor的是容器,在本例中,我并未使用容器,而是提取其行为。

2-3、Constructor Injection

Constructor Injection意指构造函数注入,主要是利用构造函数参数来注入依赖关系,构造函数注入通常是与容器紧密相关的,容器允许设计者透过特定方法,将欲注入的对象事先放入容器中,当使用端要求一个支持构造函数注入的对象时,容器中会依据目标对象的构造函数参数,一一将已放入容器中的对象注入。程序6是一个简单的容器类别,其支持Constructor Injection。

程序6

public static class Container
{
private static Dictionary<Type, object> _stores = null;
private static Dictionary<Type, object> Stores
    {
get
        {
if (_stores == null)
                _stores = new Dictionary<Type, object>();
return _stores;
        }
    }
private static Dictionary<string, object> CreateConstructorParameter(Type targetType)
    {
        Dictionary<string, object> paramArray = new Dictionary<string, object>();
        ConstructorInfo[] cis = targetType.GetConstructors();
if (cis.Length > 1)
throw new Exception(“target object has more then one constructor,container can’t peek one for you.”);
foreach (ParameterInfo pi in cis[0].GetParameters())
        {
if (Stores.ContainsKey(pi.ParameterType))
                paramArray.Add(pi.Name, GetInstance(pi.ParameterType));
        }
return paramArray;
    }
public static object GetInstance(Type t)
    {
if (Stores.ContainsKey(t))
        {
            ConstructorInfo[] cis = t.GetConstructors();
if (cis.Length != 0)
            {
                Dictionary<string, object> paramArray = CreateConstructorParameter(t);
                List<object> cArray = new List<object>();
foreach (ParameterInfo pi in cis[0].GetParameters())
                {
if (paramArray.ContainsKey(pi.Name))
                        cArray.Add(paramArray[pi.Name]);
else
                        cArray.Add(null);
                }
return cis[0].Invoke(cArray.ToArray());
            }
else if (Stores[t] != null)
return Stores[t];
else
return Activator.CreateInstance(t, false);
        }
return Activator.CreateInstance(t, false);
    }
public static void RegisterImplement(Type t, object impl)
    {
if (Stores.ContainsKey(t))
            Stores[t] = impl;
else
            Stores.Add(t, impl);
    }
public static void RegisterImplement(Type t)
    {
if (!Stores.ContainsKey(t))
            Stores.Add(t, null);
    }
}

Container类别提供了两个方法,RegisterImplement有两个重载方法,一接受一个Type对象及一个不具型物件,它会将传入的Type及对象成对的放入Stores这个Collection中,另一个重载方法则只接受一个Type对象,调用这个方法代表调用端不预先建立该对象,交由GetInstance方法来建立。GetInstance方法负责建立对象,当要求的对象类型存在于Stores记录中时,其会取得该类型的构造函数,并依据构造函数的参数,一一调用GetInstance方法来建立对象。程序7是使用这个Container的范例。

程序7

class Program
{
static void Main(string[] args)
    {
        Container.RegisterImplement(typeof(InputAccept));
        Container.RegisterImplement(typeof(IDataProcessor), new PromptDataProcessor());
        InputAccept accept = (InputAccept)Container.GetInstance(typeof(InputAccept));
        accept.Execute();
        Console.Read();
    }
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
public void Execute()
    {
        Console.Write(“Please Input some words:”);
string input = Console.ReadLine();
        input = _dataProcessor.ProcessData(input);
        Console.WriteLine(input);
    }
public InputAccept(IDataProcessor dataProcessor)
    {
        _dataProcessor = dataProcessor;
    }
}
public interface IDataProcessor
{
string ProcessData(string input);
}
public class DummyDataProcessor : IDataProcessor
{
    #region IDataProcessor Members
public string ProcessData(string input)
    {
return input;
    }
    #endregion
}
public class PromptDataProcessor : IDataProcessor
{
    #region IDataProcessor Members
public string ProcessData(string input)
    {
return “your input is: ” + input;
    }
    #endregion
}

2-4、Setter Injection

Setter Injection意指设值注入,主要概念是透过属性的途径,将依赖对象注入目标对象中,与Constructor Injection模式一样,这个模式同样需要容器的支持,程序8是支持Setter Injection的Container程序行表。

程序8

public static class Container
{
private static Dictionary<Type, object> _stores = null;
private static Dictionary<Type, object> Stores
    {
get
        {
if (_stores == null)
                _stores = new Dictionary<Type, object>();
return _stores;
        }
    }
public static object GetInstance(Type t)
    {
if (Stores.ContainsKey(t))
        {
if (Stores[t] == null)
            {
object target = Activator.CreateInstance(t, false);
foreach (PropertyDescriptor pd in TypeDescriptor.GetProperties(target))
                {
if (Stores.ContainsKey(pd.PropertyType))
                        pd.SetValue(target, GetInstance(pd.PropertyType));
                }
return target;
            }
else
return Stores[t];
        }
return Activator.CreateInstance(t, false);
    }
public static void RegisterImplement(Type t, object impl)
    {
if (Stores.ContainsKey(t))
            Stores[t] = impl;
else
            Stores.Add(t, impl);
    }
public static void RegisterImplement(Type t)
    {
if (!Stores.ContainsKey(t))
            Stores.Add(t, null);
    }
}

程序代码与Constructor Injection模式大致相同,两者差异之处仅在于Constructor Injection是使用构造函数来注入,Setter Injection是使用属性来注入,程序9是使用此Container的范例。

程序9

class Program
{
static void Main(string[] args)
    {
        Container.RegisterImplement(typeof(InputAccept));
        Container.RegisterImplement(typeof(IDataProcessor), new PromptDataProcessor());
        InputAccept accept = (InputAccept)Container.GetInstance(typeof(InputAccept));
        accept.Execute();
        Console.Read();
    }
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
public IDataProcessor DataProcessor
    {
get
        {
return _dataProcessor;
        }
set
        {
            _dataProcessor = value;
        }
    }
public void Execute()
    {
        Console.Write(“Please Input some words:”);
string input = Console.ReadLine();
        input = _dataProcessor.ProcessData(input);
        Console.WriteLine(input);
    }
}

2-5、Service Locator

在Martain Fowler的文章中,Dependency Injection并不是唯一可以将对象依赖关系降低的方式,另一种Service Locator架构也可以达到同样的效果,从架构角度来看,Service Locator是一个服务中心,设计者预先将Servcie对象推入Locator容器中,在这个容器内,Service是以Key/Value方式存在。欲使用该Service对象的对象,必须将依赖关系建立在Service Locator上,也就是说,不是透过构造函数、属性、或是方法来取得依赖对象,而是透过Service Locator来取得。