[轉]Object Builder Application Block
文/黃忠成 ;2006/9/21
原文鏈接:http://blog.csdn.net/Code6421/archive/2006/09/25/1282150.aspx
整理:呂震宇
三、ObjectBuilder Application Block
ObjectBuilder一開(kāi)始出現于Microsoft所提出的Composite UI Application Block,主司對象的建立及釋放工作, 它實(shí)現了本文前面所提及的Dependency Injection概念,同時(shí)在架構上提供了高度的延展性。運用ObjectBuilder來(lái)建立對象,設計師可以透過(guò)程序或組態(tài)文件,對對象建立與釋放的流程進(jìn)行細部的調整,例如改變對象建立時(shí)所調用的Constructor(構造函數),調整傳入的參數,于對象建立后調用特定方法等等。鑒于ObjectBuilder的功能逐漸完整,加上社群對于Dependency Injection實(shí)現對象的強烈需求,Microsoft正式將ObjectBuilder納入Enterprise Library 2006中,并修改Caching、Logger、Security、Data Access等Application Block的底層,令其于ObjectBuilder整合,以此增加這些Application Block的延展性。就官方文件的說(shuō)明,ObjectBuilder Application Block提供以下的功能。
- 允許要求一個(gè)抽象對象或接口,ObjectBuilder會(huì )依據程序或組態(tài)文件的設定,傳回一個(gè)實(shí)體對象。
- 回傳一個(gè)既存對象,或是每次回傳一個(gè)新的對象(多半用于Dependency、Singleton情況,稍后會(huì )有詳細說(shuō)明)。
- 透過(guò)特定的Factory建立一個(gè)對象,這個(gè)Factory可以依據組態(tài)文件的設定來(lái)建立對象(CustomFactory,隸屬于Enterprise Common Library)。
- 當物件擁有一個(gè)以上的構造函數時(shí),依據已有的參數,自動(dòng)選取兼容的構造函數來(lái)建立要求的對象。(Consturctor Injection)
- 允許對象于建立后,透過(guò)程序或組態(tài)文件來(lái)賦值至屬性,或是調用特定的方法。(Setter Injection、Interface Injection)
- 提供一組Attribute,讓設計師可以指定需要Injection的屬性,亦或是于對象建立后需要調用的方法,也就是使用Reflection來(lái)自動(dòng)完成Injection動(dòng)作。
- 提供IBuilerAware接口,實(shí)現此接口的對象,ObjectBuilder會(huì )于建立該對象后,調用OnBuildUp或是OnTearDown方法。
- 提供TearDown機制,按建立對象的流程,反向釋放對象。
對于多數讀者來(lái)說(shuō),這些官方說(shuō)明相當的隱誨,本文嘗試由架構角度切入,討論ObjectBuidler的主要核心概念,再透過(guò)實(shí)現讓讀者們了解,該如何使用ObjectBuidler。
3-1、The Architecture of Object Builder
圖2
圖2是ObjectBuilder中四個(gè)主要核心對象的示意圖,BuidlerContext是一個(gè)概念型的環(huán)境對象,在這個(gè)對象中,包含著(zhù)一組Strategys對象,一組Polices對象,一個(gè)Locator對象, ObjectBuidler采用Strategys Pipeline(策略流)概念,設計師必須透過(guò)Strategy串行來(lái)建立對象,而Strategy會(huì )透過(guò)Polices來(lái)尋找『類(lèi)型/id』對應的Policy對象,使用 它來(lái)協(xié)助建立指定的對象。此處有一個(gè)必須特別提出來(lái)討論的概念,Strategy在架構上是與類(lèi)型無(wú)關(guān)的,每個(gè)BuidlerContext會(huì )擁有一群Strategys對象,我們透過(guò)這個(gè)Strategys對象來(lái)建立任何類(lèi)型的對象,不管建立的對象類(lèi)型為何,都會(huì )通過(guò)這個(gè)Strategys Pipeline。這意味著(zhù),當我們希望于建立A類(lèi)型對象后調用方法A1,于建立B類(lèi)型對象后調用方法 B1時(shí),負責調用方法的Strategy對象會(huì )需要一個(gè)機制來(lái)判別該調用那個(gè)方法,那就是Policy對象,BuilderContext中擁有一個(gè)Polices對象,其中存放著(zhù)與『類(lèi)型/id』對應的Policy對象,如圖3所示。
圖3
值得一提的是,Policy是以Type/id方式,也就是『類(lèi)型/id』方式來(lái)存放,這種做法不只可以讓不同類(lèi)型擁有各自的Policy,也允許同類(lèi)型但不同id擁有各自的Policy。ObjectBuilder中的最后一個(gè)元素是Locator,Locator對象在ObjectBuidler中扮演著(zhù)前述的Service Locator角色,設計師可以用Key/Value的方式,將對象推入Locator中,稍后再以Key值來(lái)取出使用。
3-2、Strategys
ObjectBuilder內建了許多Strategy,這些Strategy可以大略分成四種類(lèi)型,如圖4。
圖4
Pre-Creation意指對象被建立前的初始動(dòng)作,參與此階段的Strategy有:TypeMappingStrategy、PropertyReflectionStrategy、ConstructorReflectionStrategy、MethodReflectionStrategy及SingletonStrategy,稍后我們會(huì )一一檢視 它們。
Creation類(lèi)型的Strategy主要工作在于建立對象,它會(huì )利用Pre-Creation Strategys所準備的參數來(lái)建立對象,ObjectBuilder就是運用Pre-Creation的ConstructorReflectionStrategy及CreationStrategy來(lái)完成Constructor Injection動(dòng)作。
當對象建立后,會(huì )進(jìn)入初始化階段,這就是Initialization Strategy階段,在此階段中,PropertySetterStrategy會(huì )與PropertyReflectionStrategy合作,完成Setter Injection。而MethodExecutionStrategy則會(huì )與MethodReflectionStrategy合作,在對象建立后,調用特定的方法,也就是Method Injection(視使用方式,Interface Injection是以此種方式完成的)。
- Post-Initialization Strategy
在對象建立并完成初始化動(dòng)作后,就進(jìn)入了Post-Initialization Strategy階段,在此階段中,BuilderAwareStrategy會(huì )探詢(xún)已建立的對象是否實(shí)現了IBuilderAware接口,是的話(huà)就調用IBuilderAware.OnBuildUp方法。
先前曾經(jīng)提過(guò),ObjectBuidler在建立對象時(shí),會(huì )一一調用所有Strategy來(lái)建立對象,同樣的!當釋放對象時(shí),ObjectBuilder也會(huì )進(jìn)行同樣的動(dòng)作,不過(guò)方向是相反的,在內建的Strategy中,只有BuilderAwareStrategy會(huì )參與對象釋放的動(dòng)作,在對象釋放時(shí),BuilderAwareStrategy會(huì )探詢(xún)欲釋放的對象是否實(shí)現了IBuidlerAware接口,是的話(huà)就調用IBuidlerAware.OnTearDown方法。
3-3、A Simple Application
再怎么詳細的說(shuō)明,少了一個(gè)實(shí)例就很難讓人理解,本節以一個(gè)簡(jiǎn)單的ObjectBuidler應用實(shí)例開(kāi)始,一步步帶領(lǐng)讀者進(jìn)入ObjectBuilder的世界。
程序10
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace SimpleApp
{
class Program
{
static void Main(string[] args)
{
Builder builder = new Builder();
TestObject obj = builder.BuildUp<TestObject>(new Locator(), null, null);
obj.SayHello();
Console.ReadLine();
}
}
public class TestObject
{
public void SayHello()
{
Console.WriteLine("TEST");
}
}
}
這是一個(gè)相當陽(yáng)春的例子,在程序一開(kāi)始時(shí)建立了一個(gè)Builder對象,它是ObjectBuilder所提供的Facade對象,其會(huì )預先建立一般常用的Strategy串行,并于BuilderUp方法被調用時(shí),建立一個(gè)BuilderContext對象,并將Srategy串行及Polices串行指定給該BuilderContext,然后進(jìn)行對象的建立工作。
要了解前面的例子中,TestObject對象究竟是如何被建立起來(lái)的,首先必須深入Builder對象的建構動(dòng)作。
private StrategyList<TStageEnum> strategies = new StrategyList<TStageEnum>();
public BuilderBase()
{
}
public PolicyList Policies
{
get { return policies; }
}
public StrategyList<TStageEnum> Strategies
{
get { return strategies; }
}
public Builder(IBuilderConfigurator<BuilderStage> configurator)
{
Strategies.AddNew<TypeMappingStrategy>(BuilderStage.PreCreation);
Strategies.AddNew<SingletonStrategy>(BuilderStage.PreCreation);
Strategies.AddNew<ConstructorReflectionStrategy>(BuilderStage.PreCreation);
Strategies.AddNew<PropertyReflectionStrategy>(BuilderStage.PreCreation);
Strategies.AddNew<MethodReflectionStrategy>(BuilderStage.PreCreation);
Strategies.AddNew<CreationStrategy>(BuilderStage.Creation);
Strategies.AddNew<PropertySetterStrategy>(BuilderStage.Initialization);
Strategies.AddNew<MethodExecutionStrategy>(BuilderStage.Initialization);
Strategies.AddNew<BuilderAwareStrategy>(BuilderStage.PostInitialization);
Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
if (configurator != null)
configurator.ApplyConfiguration(this);
}
當Buidler對象被建立時(shí),其構造函數會(huì )將前面所提及的幾個(gè)Strategys加到Strategies這個(gè)StrategyList Collection對象中,待BuildUp方法被調用時(shí)指定給新建立的BuilderContext對象。
public TTypeToBuild BuildUp<TTypeToBuild>(IReadWriteLocator locator,
string idToBuild, object existing, params PolicyList[] transientPolicies)
{
return (TTypeToBuild)BuildUp(locator, typeof(TTypeToBuild), idToBuild,
existing, transientPolicies);
}
public virtual object BuildUp(IReadWriteLocator locator, Type typeToBuild,
string idToBuild, object existing, params PolicyList[] transientPolicies)
{
....................
return DoBuildUp(locator, typeToBuild, idToBuild, existing, transientPolicies);
...................
}
private object DoBuildUp(IReadWriteLocator locator, Type typeToBuild,
string idToBuild, object existing, PolicyList[] transientPolicies)
{
IBuilderStrategyChain chain = strategies.MakeStrategyChain();
....................
IBuilderContext context = MakeContext(chain, locator, transientPolicies);
....................
object result = chain.Head.BuildUp(context, typeToBuild, existing, idToBuild);
....................
}
private IBuilderContext MakeContext(IBuilderStrategyChain chain,
IReadWriteLocator locator, params PolicyList[] transientPolicies)
{
....................
return new BuilderContext(chain, locator, policies);
}
當Builder的泛型方法BuildUp方法被調用后,其會(huì )調用非泛型的BuildUp方法,該方法會(huì )調用DoBuildUp方法,此處會(huì )透過(guò)strategies(先前于Builder構造函數時(shí)初始化的StrategyList對象)來(lái)取得Strategys串行,并指定給稍后由MakeContext方法建立的BuilderContext,最后調用Strategy串行中第一個(gè)Strategy的BuildUp方法來(lái)進(jìn)行對象的建立動(dòng)作。在這一連串的動(dòng)作中,我們可以厘清幾個(gè)容易令人混淆的設計,第一!我們是透過(guò)Strategy串行,也就是IBuidlerStrategyChain.Head.BuildUp來(lái)建立對象,這個(gè)Head屬性就是Strategy串行中的第一個(gè)Strategy。第二!BuilderContext的作用在于,于調用各個(gè)Strategy.BuildUp方法時(shí),給予 它們存取此次建立動(dòng)作所使用的Strategys及Policies等對象的機會(huì )。
現在,我們弄清楚了Strategy的用途,BuilderContext的真正涵意,但還有兩個(gè)元素尚未厘清,其中之一就是Policy對象,前面曾經(jīng)稍微提過(guò),Strategy是與類(lèi)型無(wú)關(guān)的設計概念,因此為了針對不同類(lèi)型做個(gè)別的處理,我們需要另一個(gè)與類(lèi)型相關(guān)的設計,那就是Policy對象,要確認這點(diǎn),必須重返Builder的構造函數。
public Builder(IBuilderConfigurator<BuilderStage> configurator)
{
..................
Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
.................
}
這里調用了Policies的SetDefault方法,Policies是一個(gè)PolicyList對象,其提供了推入(Set、SetDefault)及取出(Get)方法,允許設計者針對所有『類(lèi)型/id』及特定『類(lèi)型/id』指定對應的IBuilderPolicy對象,那這有什么用呢?這個(gè)問(wèn)題可以由CreationStrategy類(lèi)別中的以下這段程序代碼來(lái)回答。
public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
if (existing != null)
BuildUpExistingObject(context, typeToBuild, existing, idToBuild);
else
existing = BuildUpNewObject(context, typeToBuild, existing, idToBuild);
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
[SecurityPermission(SecurityAction.Demand, Flags = SecurityPermissionFlag.SerializationFormatter)]
private object BuildUpNewObject(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
{
ICreationPolicy policy = context.Policies.Get<ICreationPolicy>(typeToBuild, idToBuild);
.........................
InitializeObject(context, existing, idToBuild, policy);
return existing;
}
private void InitializeObject(IBuilderContext context, object existing, string id, ICreationPolicy policy)
{
.........................
ConstructorInfo constructor = policy.SelectConstructor(context, type, id);
.........................
object[] parms = policy.GetParameters(context, type, id, constructor);
.........................
method.Invoke(existing, parms);
}
如你所見(jiàn),CreationStrategy于建立對象時(shí),會(huì )由Policies中取出『類(lèi)型/id』對應的ICreationPolicy對象,接著(zhù)利用 它來(lái)取得ConstructorInfo(構造函數方法),再以GetParameters方法來(lái)取得構造函數所需的參數,最后調用此構造函數。這段程序代碼告訴我們Policy的真正用途,就是用來(lái)協(xié)助Strategy于不同『類(lèi)型/id』對象建立時(shí),采取不同的動(dòng)作,這也就是說(shuō),Strategy與Policy通常是成對出現的。
最后一個(gè)尚未弄清楚的關(guān)鍵元素是Locator,我們于調用Builder的BuildUp方法時(shí),建立了一個(gè)Locator對象并傳入該方法,這是用來(lái)做什么的呢?在ObjectBuilder中,Locator扮演兩種角色,第一個(gè)角色是提供一個(gè)對象容器供Strategy使用,這點(diǎn)可以透過(guò)以下程序了解。
public class SingletonStrategy : BuilderStrategy
{
public override object BuildUp(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
{
DependencyResolutionLocatorKey key = new DependencyResolutionLocatorKey(typeToBuild, idToBuild);
if (context.Locator != null && context.Locator.Contains(key, SearchMode.Local))
{
TraceBuildUp(context, typeToBuild, idToBuild, "");
return context.Locator.Get(key);
}
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
}
SingletonStrategy是一個(gè)用來(lái)維持某一個(gè)對象只能有一份實(shí)體存在,當此Strategy被喚起時(shí),其會(huì )先至Locator尋找目前要求的對象是否已被建立,是的話(huà)就取出該對象并傳回。Locator同時(shí)也可以作為一個(gè)Service Locator,這點(diǎn)可以由以下程序代碼來(lái)驗證。
locator.Add("Test",new TestObject());
.............
TestObject obj = locator.Get<TestObject>("Test");
當然,這種手法有一個(gè)問(wèn)題,那就是TestObject對象是預先建立后放在Locator中,這并不是一個(gè)好的設計,后面的章節我們會(huì )提出將Service Locator與Dependency Injection整合的手法。
PS:ObjectBuidler的Locator離完善的Service Locator還有段距離。
四、Dependency Injection With ObjectBuilder
ObjectBuilder支持Dependency Injection中定義的三種Injection模式,本章將一一介紹如何運用ObjectBuilder來(lái)實(shí)現。
4-1、Constructor Injection
Constructor Injection的精神在于使用構造函數來(lái)進(jìn)行注入動(dòng)作,本節延用InputAccept的例子,程序11是改采ObjectBuilder進(jìn)行Constructor Injection的例子。
程序11
using System;
using System.Collections.Generic;
using System.Text;
using System.Configuration;
using Microsoft.Practices.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration.ObjectBuilder;
using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
namespace OB_ConstructorInjectionTest
{
class Program
{
static void UseValueParameter(MyBuilderContext context)
{
ConstructorPolicy creationPolicy = new ConstructorPolicy();
creationPolicy.AddParameter(new ValueParameter(typeof(IDataProcessor), new PromptDataProcessor()));
context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
}
static void Main(string[] args)
{
MyBuilderContext context = new MyBuilderContext(new Locator());
context.InnerChain.Add(new CreationStrategy());
UseValueParameter(context);
InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context, typeof(InputAccept), null, null);
accept.Execute();
Console.Read();
}
}
internal class MyBuilderContext : BuilderContext
{
public IReadWriteLocator InnerLocator;
public BuilderStrategyChain InnerChain = new BuilderStrategyChain();
public PolicyList InnerPolicies = new PolicyList();
public LifetimeContainer lifetimeContainer = new LifetimeContainer();
public MyBuilderContext()
: this(new Locator())
{
}
public MyBuilderContext(IReadWriteLocator locator)
{
InnerLocator = locator;
SetLocator(InnerLocator);
StrategyChain = InnerChain;
SetPolicies(InnerPolicies);
if (!Locator.Contains(typeof(ILifetimeContainer)))
Locator.Add(typeof(ILifetimeContainer), lifetimeContainer);
}
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
public void Execute()
{
Console.Write("Please Input some words:");
string input = Console.ReadLine();
input = _dataProcessor.ProcessData(input);
Console.WriteLine(input);
}
public InputAccept(IDataProcessor dataProcessor)
{
_dataProcessor = dataProcessor;
}
}
public interface IDataProcessor
{
string ProcessData(string input);
}
public class DummyDataProcessor : IDataProcessor
{
#region IDataProcessor Members
public string ProcessData(string input)
{
return input;
}
#endregion
}
public class PromptDataProcessor : IDataProcessor
{
#region IDataProcessor Members
public string ProcessData(string input)
{
return "your input is: " + input;
}
#endregion
}
}
程序于一開(kāi)始時(shí),建立了一個(gè)MyBuilderContext對象,會(huì )自行建立BuilderContext對象而不使用Builder對象的目的很單純,就是為了厘清個(gè)別Strategy究竟做了那些事,這點(diǎn)在使用Builder對象時(shí),會(huì )因為內建的Strategy都已加入,而顯得有些模糊。在MyBuilderContext對象建立后,此處將一個(gè)CreationStrategy加到Strategy串行中,CreationStrategy這個(gè)Strategy被歸類(lèi)為Creation階段,是真正建立對象的Strategy,緊接著(zhù)UseValueParameter方法會(huì )被調用,這個(gè)方法中建立了一個(gè)ConstructorPolicy對象,并調用其AddParameter方法,加入一個(gè)ValueParameter對象,這個(gè)ValueParameter對象就對應著(zhù)InputAccept的構造函數所需的參數,CreationStrategy于對象建立后,會(huì )透過(guò)BuilderContext的Policies來(lái)取得『類(lèi)型/id』對應的ICreationPolicy對象(本例就是ConstructorPolicy對象),然后調用ICreationPolicy.SelectionConstructor方法,這個(gè)方法必須根據調用者已用ICreationPolicy.AddParameter所傳入的參數來(lái)選擇正確的構造函數,然后再調用這個(gè)構造函數并填入參數值來(lái)完成對象建立工作,圖5是這整個(gè)流程的示意圖。
圖5
圖中讀者可能會(huì )有所迷惑的是,FormatterServices.GetSafeUninitializedObject方法是何作用?這是.NET Framework中一個(gè)建立對象的途徑,與一般new或是Activator.CreateInstance方式不同,GetSafeUninitializedObject方法并不會(huì )觸發(fā)該對象的構造函數,只是單純的將對象建立起來(lái)而已,因此CreationStrategy才必須于最后調用對應的構造函數。
程序11中使用了一個(gè)ValueParameter對象,要知道這個(gè)對象的作用,我們得先了解Parameter在ObjectBuilder中所代表的意義,在三種注入模式中,有一個(gè)共通的規則,就是需要有參數來(lái)注入,Constructor Injection是透過(guò)構造函數參數注入,而Interface Injection則是透過(guò)函數參數注入,Setter Injection則是透過(guò)屬性注入,因此參數是這三種注入模式都會(huì )用到的觀(guān)念,所以ObjectBuilder定義了IParameter接口,并提供一組實(shí)現此接口的參數對象,于注入時(shí)期由這些參數對象來(lái)取得參數值,如圖6。
圖6
這是一個(gè)最簡(jiǎn)單的Paramter對象,構造函數如下所示:
public ValueParameter(Type valueType, object value)
它的GetValue方法僅是將構造函數傳入的value對象傳回而已。
DependencyParameter是一個(gè)功能強大的Parameter對象,程序12是以DependencyParameter來(lái)取代ValueParameter完成Constructor Injection的例子。
程序12
static void UseDependencyParameter(MyBuilderContext context)
{
ConstructorPolicy creationPolicy = new ConstructorPolicy();
creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor),null,
typeof(PromptDataProcessor),NotPresentBehavior.CreateNew,SearchMode.Local));
context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
ConstructorPolicy creationPolicy2 = new ConstructorPolicy();
context.Policies.Set<ICreationPolicy>(creationPolicy2, typeof(PromptDataProcessor),null);
}
讀者可以發(fā)現,DependencyParameter并未要求建構者傳入任何對象實(shí)體,而是要求建構者傳入注入時(shí)對應的參數類(lèi)型、參數名稱(chēng)、實(shí)體類(lèi)型、NotPersentBehavoir及SearchMode等參數,下面的程序行表是DependencyParameter的構造函數:
public DependencyParameter(Type parameterType, string name,
Type createType, NotPresentBehavior notPresentBehavior, SearchMode searchMode)
第一個(gè)參數是參數的類(lèi)型,第二個(gè)參數是參數的名稱(chēng),當ConstructorPolicy于SelectConstructor方法時(shí),會(huì )依據這兩個(gè)參數來(lái)選取適合的構造函數,第三個(gè)參數是實(shí)體對象的類(lèi)型,以本例來(lái)說(shuō),就是以PromptDataProcessor這個(gè)類(lèi)型建立對象來(lái)傳入需要IDataProcessor類(lèi)型的構造函數、方法或屬性,第四個(gè)參數則影響了DependencyParameter的取值動(dòng)作,預設情況下,DependencyParameter會(huì )先至Locator中取值,這個(gè)動(dòng)作會(huì )受到第五個(gè)參數:SearchMode的影響(稍后會(huì )介紹這一部份),如果找不到的話(huà),就會(huì )依據此參數值來(lái)做動(dòng)作,NotPersentBehavior這個(gè)列舉的定義如下:
public enum NotPresentBehavior
{
CreateNew,
ReturnNull,
Throw,
}
CreateNew代表著(zhù)當DependencyParameter于Locator找不到需要的值時(shí),調用BuilderContext.HeadOfChain.BuildUp方法來(lái)建立該對象,以此例來(lái)說(shuō)即是如此,所建立對象的類(lèi)型就是PromptDataProcessor。ReturnNull則是回傳一個(gè)Null值,Throw則是直接拋出一個(gè)例外。好了,了解了整體流程后,現在讓我們一一厘清這個(gè)流程中剩下的部份,第一!于Locator找尋需要的值是什么意思,試想一種情況,當我們在做Dependency Injection時(shí),是否有某些欲注入對象是可重用的,也就是該對象可以只建立一個(gè),注入多個(gè)不同的對象,讓這些對象共享這個(gè)注入對象,這就是DependencyParameter會(huì )先至Locator中找尋已推入的注入對象的原因,請參考程序13的例子。
程序13
static void UseDependencyParameter(MyBuilderContext context)
{
context.InnerLocator.Add(new DependencyResolutionLocatorKey(typeof(IDataProcessor), null),
new PromptDataProcessor());
ConstructorPolicy creationPolicy = new ConstructorPolicy();
creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor), null,
typeof(PromptDataProcessor), NotPresentBehavior.CreateNew, SearchMode.Local));
context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
ConstructorPolicy creationPolicy2 = new ConstructorPolicy();
context.Policies.Set<ICreationPolicy>(creationPolicy2, typeof(PromptDataProcessor), null);
}
這個(gè)例子預先建立了一個(gè)PromptDataProcessor對象,并以DependencyResolutionLocatorKey封裝后推入Locator中,這樣一來(lái),當DependencyParameter取值時(shí),就會(huì )依據參數的『類(lèi)型/id』至Locator找尋需要的值,此時(shí)就會(huì )得到我們所推入的PromptDataProcessor對象,而不是建立一個(gè)新的,另外!只要于A(yíng)ddParameter所傳入的DependencyParameter是以IDataProcessor為參數類(lèi)型,并以null為id(名稱(chēng))的話(huà),那么永遠都會(huì )傳回我們所推入Locator的PromptDataProcessor 對象。第二個(gè)要厘清的是SearchMode的涵意,在ObjectBuilder的架構上,Locator是可以有Parent/Child關(guān)系的,當DependencyParameter要找尋需要的對象時(shí),如果SearchMode是Local的話(huà),那么這個(gè)搜尋動(dòng)作只會(huì )搜尋該Locator自身,如果是Up的話(huà),那么在該Locator自身搜尋不到時(shí),就會(huì )往Parent Locator搜尋。第三個(gè)要厘清的是第二個(gè)ConstructorPolicy的建立動(dòng)作,還記得嗎?我們提過(guò)Policy是『類(lèi)型/id』相關(guān)的,當DependencyParameter無(wú)法于Locator找到需要的對象而透過(guò)BuildUp來(lái)建立對象時(shí),該『類(lèi)型/id』同樣需要一個(gè)ICreationPolicy來(lái)對應,否則將會(huì )引發(fā)Missing Policy的例外,注意!DependencyParameter所使用的name參數必須與設定Set<ICreationPolicy>時(shí)所傳入的第三個(gè)參數相同。最后一個(gè)問(wèn)題是,如果每個(gè)『類(lèi)型/id』都要設定對應的ICreationPolicy,豈不累人,ObjectBuilder當然沒(méi)有這么不人性化,我們可以調用Policies.SetDefault來(lái)為所有『類(lèi)型/id』預設一個(gè)ICreationPolicy,如程序14所示。
程序14
static void UseDependencyParameter(MyBuilderContext context)
{
ConstructorPolicy creationPolicy = new ConstructorPolicy();
creationPolicy.AddParameter(new DependencyParameter(typeof(IDataProcessor),
null, typeof(PromptDataProcessor), NotPresentBehavior.CreateNew, SearchMode.Local));
context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
context.Policies.SetDefault<ICreationPolicy>(new ConstructorPolicy());
}
與DependencyParameter相同,CreationParameter也會(huì )透過(guò)BuildUp來(lái)建立對象,不同的是其不會(huì )先搜尋Locator,也無(wú)法作參數類(lèi)型與實(shí)體類(lèi)型對應,因此無(wú)法適用于InputAccept這種以接口為介質(zhì)的注入方式,必須與TypeMappingStrategy(后述)合用才能解決,如程序15所示。
程序15
static void UseCreationParameter(MyBuilderContext context)
{
ConstructorPolicy creationPolicy = new ConstructorPolicy();
creationPolicy.AddParameter(new CreationParameter(typeof(IDataProcessor)));
context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
TypeMappingPolicy mappingPolicy = new TypeMappingPolicy(typeof(PromptDataProcessor), null);
context.Policies.Set<ITypeMappingPolicy>(mappingPolicy, typeof(IDataProcessor), null);
context.Policies.SetDefault<ICreationPolicy>(new ConstructorPolicy());
}
static void Main(string[] args)
{
MyBuilderContext context = new MyBuilderContext(new Locator());
context.InnerChain.Add(new TypeMappingStrategy());
context.InnerChain.Add(new CreationStrategy());
UseCreationParameter(context);
InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context,
typeof(InputAccept), null, null);
accept.Execute();
Console.Read();
}
CloneParameter的構造函數接受一個(gè)IParameter參數,當其GetValue方法被調用時(shí),會(huì )透過(guò)從構造函數指定的Parameter對象來(lái)取值,如果取得的值是實(shí)現了ICloneable接口的對象時(shí),其將調用Clone方法來(lái)拷貝該值,否則傳回原值,下面的程序片斷是CloneParametr的構造函數聲明。
public CloneParameter(IParameter param)
LookupParameter的構造函數接受一個(gè)object類(lèi)型的參數,當GetValue方法被調用時(shí),會(huì )經(jīng)由Locator.Get方法,以構造函數所傳入的參數為鍵值,取得位于Locator中的值,下面的程序片斷為L(cháng)ookupParameter的構造函數聲明。
public LookupParameter(object key)
程序16則是將InputAccept范例改為使用LookupParameter的版本。
程序16
static void UseLookupParameter(MyBuilderContext context)
{
context.InnerLocator.Add("dataProcessor", new PromptDataProcessor());
ConstructorPolicy creationPolicy = new ConstructorPolicy();
creationPolicy.AddParameter(new LookupParameter("dataProcessor"));
context.Policies.Set<ICreationPolicy>(creationPolicy, typeof(InputAccept), null);
context.Policies.SetDefault<ICreationPolicy>(new ConstructorPolicy());
}
- InjectionConstructor Attribute
使用Paramerer對象來(lái)進(jìn)行Consturctor Injection時(shí),設計者必須在建立對象前,預先準備這些Parameter對象,雖然動(dòng)作不算繁鎖,但若全部對象的建立都要這么做,未免有些沒(méi)有效率,為此!ObjectBuilder提供了另一種較為簡(jiǎn)單的方法,就是利用InjectionConstructor這個(gè)Attribute,再搭配上ConstructorReflectionStrategy對象,自動(dòng)的為設計者準備這些Parmeter對象,程序17是修改為InjectionConstructor模式的版本。
程序17
static void UseInjectionConstructorAttribute(MyBuilderContext context)
{
context.InnerChain.Add(new ConstructorReflectionStrategy());
context.InnerChain.Add(new CreationStrategy());
}
..........
public class InputAccept
{
private IDataProcessor _dataProcessor;
public void Execute()
{
Console.Write("Please Input some words:");
string input = Console.ReadLine();
input = _dataProcessor.ProcessData(input);
Console.WriteLine(input);
}
[InjectionConstructor]
public InputAccept([Dependency(Name = "dataProcessor",
CreateType = typeof(PromptDataProcessor))]IDataProcessor dataProcessor)
{
_dataProcessor = dataProcessor;
}
}
要使用InjectionConstructor Attribute,我們必須在CreationStrategy這個(gè)Strategy前加入一個(gè)ConstructorReflectionStrategy對象, 它會(huì )于建立對象動(dòng)作時(shí),探詢(xún)欲建立對象類(lèi)型所提供的所有構造函數,選取已標上InjectionConstrucor Attribute的那個(gè)為指定構造函數,接著(zhù)ConstructorReflectionStrategy會(huì )探詢(xún)該構造函數的所有參數,查看是否標上Dependency Attribute,是的話(huà)就以其設定建立DependencyParameter,否則建立一個(gè)新的DependencyParameter,它會(huì )單以類(lèi)型參數來(lái)建立DependencyParameter,最后ConstructorReflectionStrategy會(huì )以這些信息來(lái)建立對應的ConstructorPolicy對象,完成整個(gè)對象建立動(dòng)作。
- Understanding Dependency Attribute
ConstructorReflectionStrategy依賴(lài)兩個(gè)關(guān)鍵的Attribute,一個(gè)是用來(lái)標示指定構造函數的InjectionConstructor Attribute,另一個(gè)則是用來(lái)標示參數該如何取得的Dependency Attribute,此Attribute有四個(gè)屬性,分別對應到DependencyParameter的四個(gè)屬性:
| DependencyAttribute | DependencyParameter | 說(shuō)明 |
| Name | Name | id(名稱(chēng)) |
| CreateType | CreateType | 欲建立對象的實(shí)體類(lèi)型 |
| NotPersentBehavior | NotPersentBehavior | 當欲建立對象無(wú)法由Locator取得時(shí)的行為模式。 |
| SearchMode | SearchMode | 對Locator的搜尋法則。 |
使用Dependency Attribute與ConsturctorReflectionStrategy模式的優(yōu)點(diǎn)是設計者不需花費時(shí)間一一建立Parameter對象,而缺點(diǎn)就是CreateType參數,由于ConstructorReflectionStrategy依賴(lài)著(zhù)Dependency Attribute的CreateType參數來(lái)決定實(shí)際建立對象的類(lèi)型,這使得設計者必須在標示Dependency Attribute時(shí),一并指定這個(gè)參數,否則ConstructorReflectionStrategy將會(huì )以參數類(lèi)型做為建立實(shí)際對象時(shí)的類(lèi)型,而在本例中,我們無(wú)法建立一個(gè)IDataProcessor對象,這點(diǎn)降低了程序的可訂制性。那這要如何解決呢?簡(jiǎn)單的方法是撰寫(xiě)一個(gè)新的Dependency Attribute、或是使用TypeMappingStrategy,復雜的則是撰寫(xiě)一個(gè)新的ConstructorReflectionStrategy,后面的章節我們會(huì )再重訪(fǎng)這個(gè)問(wèn)題。
- Injection with DependencyResolutionLocator
前面談到DependencyParameter時(shí)曾經(jīng)提過(guò),它會(huì )先至Locator中搜尋需要的參數值,那么這也意味著(zhù),在使用ConstructorReflectionStrategy時(shí),我們可以將參數值先行推入Locator中,這樣就可以避開(kāi)指定CreateType了,如程序18所示。
程序18
static void UseDependencyResolution(MyBuilderContext context)
{
context.InnerChain.Add(new ConstructorReflectionStrategy());
context.InnerChain.Add(new CreationStrategy());
context.InnerLocator.Add(new DependencyResolutionLocatorKey(typeof(IDataProcessor),
"dataProcessor"), new PromptDataProcessor());
}
[InjectionConstructor]
public InputAccept([Dependency(Name = "dataProcessor")]IDataProcessor dataProcessor)
{
...................
}
當然,這仍然會(huì )有一個(gè)問(wèn)題,那就是必須預先建立PromptDataProcessor對象,而非于InputAccept對象建立時(shí)期建立,這是在不撰寫(xiě)自定Dependency Attribute或Strategy,亦或是使用TypeMappingStrategy情況下的簡(jiǎn)易解法。
- DefaultCreationPolicy and ConstructorPolicy
ObjectBuilder內建了兩個(gè)ICreationPolicy的實(shí)現對象,一是前面所使用的ConstructorPolicy,二是DefaultCreationPolicy,與ConstructorPolicy不同,DefaultCreationPolicy永遠使用預設的構造函數,如下所示。
public ConstructorInfo SelectConstructor(IBuilderContext context, Type type, string id)
{
if (constructor != null)
return constructor;
List<Type> types = new List<Type>();
foreach (IParameter parm in parameters)
types.Add(parm.GetParameterType(context));
return type.GetConstructor(types.ToArray());
}
而調用該構造函數時(shí)所需的參數,則直接以BuildUp方法,依據參數的『類(lèi)型/id』來(lái)建立,沒(méi)有與Parameter的互動(dòng)。
public object[] GetParameters(IBuilderContext context, Type type, string id, ConstructorInfo constructor)
{
ParameterInfo[] parms = constructor.GetParameters();
object[] parmsValueArray = new object[parms.Length];
for (int i = 0; i < parms.Length; ++i)
parmsValueArray[i] = context.HeadOfChain.BuildUp(context, parms[i].ParameterType, null, id);
return parmsValueArray;
}
由此可見(jiàn),DefaultCreationPolicy有兩個(gè)特色,一是其會(huì )選擇頂端的構造函數,二是其一律以BuidUp方法依據參數類(lèi)型來(lái)建立參數對象,不需要設計者介入。那在何種情況下選擇DefaultCreationPolicy呢?一般來(lái)說(shuō),使用ConstructorPolicy時(shí),因為其會(huì )依據設計者所加入的Parameter對象來(lái)選擇構造函數,如果設計者未準備這些,那么ConstructorPolicy將因無(wú)法取得適合的構造函數而引發(fā)例外,雖然這點(diǎn)可以經(jīng)由搭配ConstructorReflectionStrategy來(lái)解決,但使用ConstructorReflectionStrategy時(shí)必須搭配Dependency Attribtue及InjectionConstructor Attribute,所以也是個(gè)負擔。使用DefaultCreationPolicy就沒(méi)有這些問(wèn)題了,缺點(diǎn)則是無(wú)法指定實(shí)際建立的參數對象類(lèi)型,所以DefautlCreationPolicy通常被設定成預設的ICreationPolicy,主要作用在于當我們所建立的對象是簡(jiǎn)單的,只有一個(gè)構造函數,且不需要特別指定參數實(shí)際類(lèi)型時(shí),就交由它來(lái)處理,而需要特別處理的,就運用『類(lèi)型/id』對應的ConstructorPolicy或是Dependency Attribute、Injection Constructor Attrbute搭配ConstructorReflectionStrategy來(lái)處理。
4-2、Interface Injection
Interface Injection在ObjectBuidler中可以經(jīng)由Method Injection來(lái)完成,指的是在對象建立后,調用所指定的方法來(lái)完成初始化動(dòng)作,而負責這個(gè)工作的就是MethodExecutionStrategy,本節持續延應InputAccept來(lái)示范如何于ObjectBuidler中實(shí)現Interface Injection。
要實(shí)現Interface Injection,除了必須使用CreationStrategy來(lái)建立對象外,還要使用另一個(gè)Strategy:MethodExecutionStrategy, 它會(huì )在對象建立完成后,執行指定的方法,程序19是使用MethodExecutionStrategy來(lái)實(shí)現Interface Injection的例子。
程序19
static void UseMethodInfo(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new MethodExecutionStrategy());
IMethodCallInfo callInfo = new MethodCallInfo("SetDataProcessor", new ValueParameter(typeof(IDataProcessor),
new PromptDataProcessor()));
IMethodPolicy policy = new MethodPolicy();
policy.Methods.Add("SetDataProcessor", callInfo);
context.Policies.Set<IMethodPolicy>(policy, typeof(InputAccept), null);
}
static void Main(string[] args)
{
MyBuilderContext context = new MyBuilderContext(new Locator());
UseMethodInfo(context);
context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context, typeof(InputAccept), null, null);
accept.Execute();
Console.Read();
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
public void SetDataProcessor(IDataProcessor dataProcessor)
{
_dataProcessor = dataProcessor;
}
public void Execute()
{
Console.Write("Please Input some words:");
string input = Console.ReadLine();
input = _dataProcessor.ProcessData(input);
Console.WriteLine(input);
}
}
此處使用ValueParameter來(lái)進(jìn)行調用指定方法時(shí)的參數注入動(dòng)作,在使用MethodExecutionStrategy時(shí),設計者必須先行建立調用方法時(shí)所需的MethodCallInfo對象,這是一個(gè)實(shí)現IMethodInfo接口的對象,設計者必須于此對象中指定欲調用的方法、及傳入的Parameter對象,下面是MethodInfo的構造函數聲明。
public MethodCallInfo(string methodName)
public MethodCallInfo(string methodName, params object[] parameters)
public MethodCallInfo(string methodName, params IParameter[] parameters)
public MethodCallInfo(string methodName, IEnumerable<IParameter> parameters)
public MethodCallInfo(MethodInfo method)
public MethodCallInfo(MethodInfo method, params IParameter[] parameters)
public MethodCallInfo(MethodInfo method, IEnumerable<IParameter> parameters)
MethodInfo擁有許多重載的構造函數,大概分成兩大類(lèi):方法名稱(chēng)及MethodInfo對象,每類(lèi)會(huì )分成四個(gè),分別是無(wú)參數、使用params傳入參數值、使用params傳入IParamete對象、傳入IEnumerable<IParameter>對象。在MethodInfo對象建立后,接著(zhù)就要將這些對象傳入IMethodPolicy對象,并指定給context.Policies對象,這樣就完成了Interface Injection的準備動(dòng)作,之后建立InputAccept對象后,SetDataProcess方法就會(huì )被調用,同時(shí)會(huì )傳入指定的PromptDataProcessor對象。
- How MethodExecutionStrategy Working?
當MethodExecutionStrategy的BuildUp方法被調用時(shí),會(huì )透過(guò)context.Policies來(lái)取得類(lèi)型對應的IMethodPolicy對象,如下所示。
IMethodPolicy policy = context.Policies.Get<IMethodPolicy>(type, id);
然后會(huì )透過(guò)IMethodPolicy對象來(lái)取得所有需要處理的IMethodCallInfo對象,并一一調用其SelectMethod方法來(lái)取得欲調用方法,如下所示。
MethodInfo methodInfo = methodCallInfo.SelectMethod(context, type, id);
SelectMethod方法會(huì )依據當初建立此IMethodCallInfo對象時(shí)所指定的方法名稱(chēng)、參數數量及類(lèi)型來(lái)取得對應方法的MethodInfo對象。于取得MethodInfo對象后,緊接著(zhù)就是透過(guò)IMethodCallInfo.GetParameters方法來(lái)取得調用此方法時(shí)需傳入的參數值,如下所示。
object[] parameters = methodCallInfo.GetParameters(context, type, id, methodInfo);
最后調用MethodInfo.Invoke方法來(lái)調用該方法就完成整個(gè)動(dòng)作了。
methodInfo.Invoke(obj, parameters);
好了,這就是MethodExecutionStrategy的整個(gè)流程,現在我們要厘清幾個(gè)可能會(huì )令人困惑的問(wèn)題,第一!當欲調用的方法是重載,有多個(gè)同名方法時(shí),SelectMethod依據什么來(lái)決定要調用那一個(gè)?答案是參數數量及類(lèi)型。第二!當使用Parameter對象傳入MethodCallInfo對象的構造函數時(shí),GetParameters方法會(huì )透過(guò)Parameter.GetValue來(lái)取值,那么當直接以object[]方式傳入MethodCallInfo的構造函數時(shí)呢?答案是該構造函數會(huì )逐個(gè)為傳入的object建立ValueParameter對象,如下所示。
public MethodCallInfo(string methodName, params object[] parameters)
: this(methodName, null, ObjectsToIParameters(parameters))
{
}
private static IEnumerable<IParameter> ObjectsToIParameters(object[] parameters)
{
List<IParameter> results = new List<IParameter>();
if (parameters != null)
foreach (object parameter in parameters)
results.Add(new ValueParameter(parameter.GetType(), parameter));
return results.ToArray();
}
最后一個(gè)問(wèn)題是,可以進(jìn)行一個(gè)以上的函數調用嗎?答案是可以,建立對應的MethodCallInfo對象,并加到IMethodPolicy后即可,調用的順序則是依照MethodCallInfo加入IMethodPolicy的順序。
與Constructor Injection相同,你也可以使用DependencyParameter來(lái)進(jìn)行Interface Injection動(dòng)作,如程序20。
程序20
static void UseDependencyParameter(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new MethodExecutionStrategy());
MethodCallInfo callInfo = new MethodCallInfo("SetDataProcessor",
new DependencyParameter(typeof(IDataProcessor), "dataProcessor",
typeof(PromptDataProcessor), NotPresentBehavior.CreateNew, SearchMode.Local));
IMethodPolicy policy = new MethodPolicy();
policy.Methods.Add("SetDataProcessor", callInfo);
context.Policies.Set<IMethodPolicy>(policy, typeof(InputAccept), null);
}
- use MethodReflectionStrategy
如同ConstructorReflectionStrategy的作用一樣,ObjectBuilder也提供了供Method Injection使用的MethodReflectionStrategy對象,要使用它,我們必須為欲進(jìn)行Method Injection的方法標上InjectionMethod Attribute,如程序21所示。
程序21
static void UseDependencyResolverLocator(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new MethodReflectionStrategy());
context.InnerChain.Add(new MethodExecutionStrategy());
context.InnerLocator.Add(new DependencyResolutionLocatorKey(typeof(IDataProcessor),
"dataProcessor"), new PromptDataProcessor());
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
[InjectionMethod]
public void SetDataProcessor([Dependency(Name = "dataProcessor")]IDataProcessor dataProcessor)
{
_dataProcessor = dataProcessor;
}
...........
}
本例使用DependencyResolutionLocatorKey模式進(jìn)行注入動(dòng)作,有了Constructor Injection部份的解說(shuō),相信讀者對這種模式已經(jīng)了然于胸了。
- Injection with Dependency Attribute and CreateType
同樣的,我們也可以在Dependency Attribute中指定CreateType來(lái)達到同樣的效果,如程序22所示。
程序22
static void UseDependencyAttribute(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new MethodReflectionStrategy());
context.InnerChain.Add(new MethodExecutionStrategy());
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
[InjectionMethod]
public void SetDataProcessor([Dependency(Name = "dataProcessor",
CreateType = typeof(PromptDataProcessor))]IDataProcessor dataProcessor)
{
_dataProcessor = dataProcessor;
}
.............
}
4-3、Setter Injection
ObjectBuilder使用PropertySetterStrategy來(lái)進(jìn)行Setter Injection,用法與前述的Interface Injection模式大致相同,如程序23所示。
程序23
static void UsePropertySetter(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new PropertySetterStrategy());
PropertySetterPolicy policy = new PropertySetterPolicy();
policy.Properties.Add("DataProcessor", new PropertySetterInfo("DataProcessor",
new ValueParameter(typeof(IDataProcessor), new PromptDataProcessor())));
context.Policies.Set<IPropertySetterPolicy>(policy, typeof(InputAccept), null);
}
static void Main(string[] args)
{
MyBuilderContext context = new MyBuilderContext(new Locator());
UsePropertySetter(context);
context.Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
InputAccept accept = (InputAccept)context.HeadOfChain.BuildUp(context,
typeof(InputAccept), null, null);
accept.Execute();
Console.Read();
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
public IDataProcessor DataProcessor
{
get
{
return _dataProcessor;
}
set
{
_dataProcessor = value;
}
}
public void Execute()
{
Console.Write("Please Input some words:");
string input = Console.ReadLine();
input = _dataProcessor.ProcessData(input);
Console.WriteLine(input);
}
}
設計者必須預先建立PropertySetterInfo對象,并為其指定欲設定的屬性名稱(chēng)及參數,PropertySetterInfo是一個(gè)實(shí)現了IPropertySetterInfo接口的對象,其構造函數聲明如下。
public PropertySetterInfo(string name, IParameter value)
public PropertySetterInfo(PropertyInfo propInfo, IParameter value)
有了MethodCallInfo的經(jīng)驗,讀者們對這些構造函數應該不會(huì )有任何疑惑,應該會(huì )抱怨其不像MethodCallInfo般提供那么多的選擇吧(笑)。在ProeprtySetterInfo建立后,接著(zhù)只要將其加到IPropertySetterPolicy對象中,并依『類(lèi)型/id』指定給context.Policies即可完成Setter Injection。
- How PropertySetterStrategy Work?
當PropertySetterStrategy的BuildUp方法被調用時(shí),會(huì )透過(guò)context.Policies來(lái)取得類(lèi)型對應的IPropertySetterPolicy對象,如下所示。
IPropertySetterPolicy policy = context.Policies.Get<IPropertySetterPolicy>(type, id);
然后會(huì )透過(guò)IMethodPoliIPropertySetterPolicyy對象來(lái)取得所有需要處理的IPropertySetterInfo對象,并一一調用其SelectProperty方法來(lái)取得欲設定的屬性,如下所示。
PropertyInfo propInfo = propSetterInfo.SelectProperty(context, type, id);
SelectProperty方法會(huì )依據當初建立此IPropertySetterInfo對象時(shí)所指定的屬性名稱(chēng)、參數來(lái)取得對應屬性的PropertyInfo對象。于取得PropertyInfo對象后,緊接著(zhù)就是透過(guò)IPropertySetterInfo.GetValue方法來(lái)取得設定此屬性時(shí)需傳入的值,如下所示。
object value = propSetterInfo.GetValue(context, type, id, propInfo);
最后調用PropertyInfo.SetValue方法來(lái)設定屬性值就完成整個(gè)動(dòng)作了。
propInfo.SetValue(obj, value, null);
這就是整個(gè)Setter Injection的流程,這里只有一個(gè)問(wèn)題,我們可以設定一個(gè)以上的屬性嗎?答案是肯定的,只要建立對應數量的PropertySetterInfo對象即可。
同樣的,使用DependencyParameter也可以達到同樣的效果,如程序24。
程序24
static void UseDependencyParameter(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new PropertySetterStrategy());
PropertySetterPolicy policy = new PropertySetterPolicy();
policy.Properties.Add("DataProcessor", new PropertySetterInfo("DataProcessor",
new DependencyParameter(typeof(IDataProcessor),"DataProcessor",
typeof(PromptDataProcessor),NotPresentBehavior.CreateNew,SearchMode.Local)));
context.Policies.Set<IPropertySetterPolicy>(policy, typeof(InputAccept), null);
}
- use PropertyReflectionStrategy
相對于ConsturctorReflectionStrategy及MethodReflectionStrategy,ObjectBuilder也提供了一個(gè)同類(lèi)型的PropertyReflectionStrategy,我們可以搭配Dependency Attribute及DependencyResolutionLocatorKey對象來(lái)達到同樣效果,如程序25。
程序25
static void UseDependencyResolutionLocator(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new PropertyReflectionStrategy());
context.InnerChain.Add(new PropertySetterStrategy());
context.Locator.Add(new DependencyResolutionLocatorKey(typeof(IDataProcessor),
"DataProcessor"), new PromptDataProcessor());
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
[Dependency(Name = "DataProcessor")]
public IDataProcessor DataProcessor
{
get
{
return _dataProcessor;
}
set
{
_dataProcessor = value;
}
}
.........
}
- Injection with Dependency Attribute and CreateType
我們也可以使用Dependency Attribute及CreateType參數來(lái)進(jìn)行Setter Injection,如程序26。
程序26
static void UseDependencyAttribute(MyBuilderContext context)
{
context.InnerChain.Add(new CreationStrategy());
context.InnerChain.Add(new PropertyReflectionStrategy());
context.InnerChain.Add(new PropertySetterStrategy());
}
public class InputAccept
{
private IDataProcessor _dataProcessor;
[Dependency(Name = "DataProcessor", CreateType = typeof(PromptDataProcessor))]
public IDataProcessor DataProcessor
{
get
{
return _dataProcessor;
}
set
{
_dataProcessor = value;
}
}
...............
}