Inside ObjectBuilder Part 3
来源:互联网 发布:java webservice 框架 编辑:程序博客网 时间:2024/04/30 17:00
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』標示為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 != 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<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等之類,第二個參數是對應的型別,第三個參數則是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-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 物件,讓設計者可以指定一個以上的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模式,意味所有的物件建立動作都會獲得一個新的物件實體,跟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<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
v Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1282161
- Inside ObjectBuilder Part 3
- Inside ObjectBuilder Part 3
- Inside ObjectBuilder Part 3
- Inside ObjectBuilder Part 2
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 2
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 2
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 2
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 2
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 4
- Inside ObjectBuilder Part 2
- Framework的设计与应用--基于Windows Forms的应用开发实践
- Inside ObjectBuilder 范例
- Inside ObjectBuilder Part 4
- 把自己的应用程序加载到uClinux中运行
- 路由器和IP
- Inside ObjectBuilder Part 3
- Inside ObjectBuilder Part 2
- 要共同创业
- Inside ObjectBuilder Part1
- Inside ASP.NET 2.0 – Controls Model
- Developing ASP.NET Custom Control With C# Builder
- 在 WebSphere Application Server 中处理静态内容
- 近日計劃
- .NET Framework Security Code Access Security-應用程式篇