Inside ObjectBuilder Part 3

 

Object Builder Application Block
 
/ 黃忠成    
2006/9/21

 

五、 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 』標示為 Singleto n ,設計者必須於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);
}
 
P S :注意,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 再加入 TypeMappingStrateg y 時,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 != null)
                     configurator.ApplyConfiguration(this);
}
只要傳入的 BuilderStage 是正確的,不管 TypeMappingStrategy 是加在 CreationStrategy 前面或後面,皆可正常運作。不過同一類型的 Strateg y ,但有順序需求的情況下,仍然要小心調整順序,程式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 <BuilderPolicyKey, IBuilderPolicy> 物件來儲存設計者所加入的Policy物件,其中用來作為鍵值的BuilderPolicyKey類別建構子如下。
public BuilderPolicyKey(Type policyType, Type typePolicyAppliesTo, string idPolicyAppliesTo)
第一個參數為 policyType ,也就是 ICrationPolicy ITypeMappingPolicy 等之類,第二個參數是對應的型別,第三個參數則是 i d 。設計者可以呼叫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 都必須直接或間接實作此介面,內建實作此介面的類別是 ReadableLocato r ,她是一個抽象類別。真正完成實作可用的是ReadOnlyLocator,這個Locator只允許讀取,不允許新增。
 
6-2 ReadWrite Locator
 
  ObjectBuilder 中支援讀與寫的 Locator ReadWriterLocator ,與 ReadOnlyLocator 一樣,她也是一個抽象類別,真正完成實作的是 Locator 類別。附帶一提,雖然 Locator 定義了蠻清楚的階層,但是 BuilderContext 只支援實作 IReadWriterLocator 介面的 Locato r
 
6-3 WeakRefDictionary and Locator
 
  Locator 類別是我們一直都在使用的 Locator ,她是一個繼承自 ReadWriterLocator 的類別,值得一提的是,她使用一個 WeakRefDictionary 來儲存設計者所放入 Locator 的物件, WeakRefDictionary 內部對於每個元素都會以 WeakReference 封裝,這意味著, Locator 中的元素並無法保證一直都存在,因為 CLR 會在記憶體拮据時,先行釋放 WeakRefernce 所封裝的物件,這點讀者必須謹記。
 
七、 Extending ObjectBuilder
 
   ObjectBuilder 除了支援三種 Dependency Injection 模式、 Service Locator 之外,最大的魅力應該來自於具高度延展性的架構,設計者可以透過撰寫 Strateg y 、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 Injectio n
 
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 介面,該介面必須直接或間接繼承自 IBuilderPolic y 介面,這是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, string 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 模式,意味所有的物件建立動作都會獲得一個新的物件實體,跟 ne w 、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 = false;
            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 中取出 PoolFactor y ,如果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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园的建设目标是通过数据整合、全面共享,实现校园内教学、科研、管理、服务流程的数字化、信息化、智能化和多媒体化,以提高资源利用率和管理效率,确保校园安全。 智慧校园的建设思路包括构建统一支撑平台、建立完善管理体系、大数据辅助决策和建设校园智慧环境。通过云架构的数据中心与智慧的学习、办公环境,实现日常教学活动、资源建设情况、学业水平情况的全面统计和分析,为决策提供辅助。此外,智慧校园还涵盖了多媒体教学、智慧录播、电子图书馆、VR教室等多种教学模式,以及校园网络、智慧班牌、校园广播等教务管理功能,旨在提升教学品质和管理水平。 智慧校园的详细方案设计进一步细化了教学、教务、安防和运维等多个方面的应用。例如,在智慧教学领域,通过多媒体教学、智慧录播、电子图书馆等技术,实现教学资源的共享和教学模式的创新。在智慧教务方面,校园网络、考场监控、智慧班牌等系统为校园管理提供了便捷和高效。智慧安防系统包括视频监控、一键报警、阳光厨房等,确保校园安全。智慧运维则通过综合管理平台、设备管理、能效管理和资产管理,实现校园设施的智能化管理。 智慧校园的优势和价值体现在个性化互动的智慧教学、协同高效的校园管理、无处不在的校园学习、全面感知的校园环境和轻松便捷的校园生活等方面。通过智慧校园的建设,可以促进教育资源的均衡化,提高教育质量和管理效率,同时保障校园安全和提升师生的学习体验。 总之,智慧校园解决方案通过整合现代信息技术,如云计算、大数据、物联网和人工智能,为教育行业带来了革命性的变革。它不仅提高了教育的质量和效率,还为师生创造了一个更加安全、便捷和富有智慧的学习与生活环境。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值