您的位置:首页 > 移动开发 > Objective-C

Inside ObjectBuilder Part 3

2007-12-14 17:44 399 查看
 
Object Builder Application Block /黃忠成    2006/9/21 
五、Misc 5-1SingletonStrategy   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(new SingletonPolicy(true), typeof(TestObject), null);             context.Policies.SetDefault(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( typeof(ILifetimeContainer), SearchMode.Local);                        if (lifetime != null)                      {                                 ISingletonPolicy singletonPolicy = context.Policies.Get( 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-2TypeMappingStrategy  前面的章節早已使用過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(policy, typeof(ITestInterface), null);             context.Policies.SetDefault(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(policy, typeof(ITestInterface), null)
當TypeMappingStrategy的BuildUp函式被呼叫時,她會以『型別/id』取得對應的ITypeMappingPolicy物件,透過她來取得對應的型別,之後將使用這個型別呼叫下一個Strategy的BuildUp函式,這就是Type Mapping的流程。
PS:注意,Type Mapping型別必須相容,如介面->實作、基礎類別->衍生類別。
 5-3BuildAwareStrategy  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(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-4BuildUp的第三、四個參數  截至目前為止,我們的例子在呼叫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(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(new DefaultCreationPolicy());               PropertySetterInfo pi1 = new PropertySetterInfo("ID", new ValueParameter("ID1"));             PropertySetterPolicy pp1 = new PropertySetterPolicy();             pp1.Properties.Add("ID", pi1);             context.Policies.Set(pp1, typeof(TestObject), "TO1");                 PropertySetterInfo pi2 = new PropertySetterInfo("ID", new ValueParameter("ID2"));             PropertySetterPolicy pp2 = new PropertySetterPolicy();             pp2.Properties.Add("ID", pi2);             context.Policies.Set(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(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(new SingletonPolicy(true), typeof(TestObject), "ID1");             context.Policies.Set(new SingletonPolicy(true), typeof(TestObject), "ID2");             context.Policies.SetDefault(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-5StrategyList   在本文一開始的範例中,我們使用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 configurator) {            Strategies.AddNew(BuilderStage.PreCreation);            Strategies.AddNew(BuilderStage.PreCreation);            Strategies.AddNew(BuilderStage.PreCreation);            Strategies.AddNew(BuilderStage.PreCreation);            Strategies.AddNew(BuilderStage.PreCreation);            Strategies.AddNew(BuilderStage.Creation);            Strategies.AddNew(BuilderStage.Initialization);            Strategies.AddNew(BuilderStage.Initialization);            Strategies.AddNew(BuilderStage.PostInitialization);            Policies.SetDefault(new DefaultCreationPolicy()); if (configurator != null)                      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(policy, typeof(ITestInterface), null);             ITestInterface obj1 = builder.BuildUp(new Locator(), null, null);             Console.Read();         }     }       public class MyBuilder : BuilderBase     {              public MyBuilder()             : this(null)         {         }          public MyBuilder(IBuilderConfigurator configurator)         {             Strategies.AddNew(BuilderStage.Creation);             Strategies.AddNew(BuilderStage.PreCreation);             Strategies.AddNew(BuilderStage.PreCreation);             Strategies.AddNew(BuilderStage.PreCreation);             Strategies.AddNew(BuilderStage.PreCreation);             Strategies.AddNew(BuilderStage.PreCreation);                        Strategies.AddNew(BuilderStage.Initialization);             Strategies.AddNew(BuilderStage.Initialization);             Strategies.AddNew(BuilderStage.PostInitialization);                          Policies.SetDefault(new DefaultCreationPolicy());               if (configurator != null)                 configurator.ApplyConfiguration(this);         }   }       public interface ITestInterface     {     }       public class TestObject : ITestInterface     {     } }
 5-6PolicyList  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 policy, Type typePolicyAppliesTo, string idPolicyAppliesTo)                                 where TPolicyInterface : IBuilderPolicy {            Set(typeof(TPolicyInterface), policy, typePolicyAppliesTo, idPolicyAppliesTo); }
設計者可以透過PolicyList.Get函式來取得對應的Policy物件,該函式如下所示。
public TPolicyInterface Get(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-1Readonly Locator  ObjectBuidler定義了一個IReadableLocator介面,所有的Locator都必須直接或間接實作此介面,內建實作此介面的類別是ReadableLocator,她是一個抽象類別。真正完成實作可用的是ReadOnlyLocator,這個Locator只允許讀取,不允許新增。 6-2ReadWrite Locator  ObjectBuilder中支援讀與寫的Locator是ReadWriterLocator,與ReadOnlyLocator一樣,她也是一個抽象類別,真正完成實作的是Locator類別。附帶一提,雖然Locator定義了蠻清楚的階層,但是BuilderContext只支援實作IReadWriterLocator介面的Locator。 6-3WeakRefDictionary 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-1EventSetterStrategy  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 Events { get;} }
針對同一『型別/id』物件可能需要注入一個以上的事件,此介面定義了一個Dictionary物件,讓設計者可以指定一個以上的Event Injection動作,36是此介面的實作。程式36
public sealed class EventSetterPolicy : IEventSetterPolicy {      private Dictionary _events = new Dictionary();      #region IEventPolicy Members        public Dictionary 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(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(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(policy, typeof(TestObject), null);               TestObject obj = builder.BuildUp(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-2PoolStrategy    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 {     ///     /// a interface to be implement by Object Factory,     /// DAL use object factory to speed object constructing.     ///     public interface IObjectFactory     {                ///         /// acquire a object.         ///         ///object Type         ///object         object AcquireObject(Type type);         ///         /// release a object.         ///         ///a object to releasing         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 _factoryMap = new WeakRefDictionary();         private bool _poolObjectCreating = false;           public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)         {             if (!_poolObjectCreating)             {                 IPoolPolicy policy = context.Policies.Get(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(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類別物件,在物件實體於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(BuilderStage.PreCreation);               IPoolPolicy policy = new PoolPolicy(true);             builder.Policies.Set(policy, typeof(TestObject), null);               Locator locator = new Locator();             TestObject obj1 = builder.BuildUp(locator, null, null);             TestObject obj2 = builder.BuildUp(locator, null, null);             builder.TearDown(locator, obj1);             builder.TearDown(locator, obj2);             TestObject obj3 = builder.BuildUp(locator, null, null);             if (obj3 == obj1 || obj3 == obj2)                 Console.WriteLine("Pooled");             Console.ReadLine();           }     }       public class TestObject     {     } }
圖9是執行結果。圖9



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1282161
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: