First we try, then we trust

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

[]Object Builder Application Block

文/黄忠成 ;2006/9/21

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

整理:吕震宇

五、Misc

5-1、SingletonStrategy

SingletonStrategy可于对象实体首次建立后,将实体保留在Context中的Locator内的ILifetimeContainer对象中,之后相同型态、id相同的对象建立动作,都是传回这个对象,这是Singleton模式的实现,如程序27。

程序27

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;

namespace OB_SingletonTest
{
    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), null);
            context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());

            TestObject obj1 = (TestObject)context.HeadOfChain.BuildUp(context,
                typeof(TestObject), null, null);
            TestObject obj2 = (TestObject)context.HeadOfChain.BuildUp(context,
                typeof(TestObject), null, null);
            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
    {
    }
}

要将一个『类型/id』标示为Singleton,设计者必须于Strategy串行中加入SingletonStrategy对象,并建立一个SingletonPolicy对象,这是一个实现了ISingletonPolicy接口的类别,其构造函数如下。

public SingletonPolicy(bool isSingleton);

CreatationStrategy在建立对象后,会从context.Policies中取出『类型/id』对应的ISingletonPolicy对象,以其IsSingleton属性来决定建立的对象是否为Singleton模式,是的话就将该对象实体填入ILifetimeContainer中,同时以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

posted on 2007-02-06 10:45  吕震宇  阅读(3022)  评论(1编辑  收藏  举报