Object Builder Application Block (2)
三、ObjectBuilder Application Block
ObjectBuilder一開(kāi)始出現(xiàn)于Microsoft所提出的Composite UI Application Block,主司對(duì)象的建立及釋放工作, 它實(shí)現(xiàn)了本文前面所提及的Dependency Injection概念,同時(shí)在架構(gòu)上提供了高度的延展性。運(yùn)用ObjectBuilder來(lái)建立對(duì)象,設(shè)計(jì)師可以透過(guò)程序或組態(tài)文件,對(duì)對(duì)象建立與釋放的流程進(jìn)行細(xì)部的調(diào)整,例如改變對(duì)象建立時(shí)所調(diào)用的Constructor(構(gòu)造函數(shù)),調(diào)整傳入的參數(shù),于對(duì)象建立后調(diào)用特定方法等等。鑒于ObjectBuilder的功能逐漸完整,加上社群對(duì)于Dependency Injection實(shí)現(xiàn)對(duì)象的強(qiáng)烈需求,Microsoft正式將ObjectBuilder納入Enterprise Library 2006中,并修改Caching、Logger、Security、Data Access等Application Block的底層,令其于ObjectBuilder整合,以此增加這些Application Block的延展性。就官方文件的說(shuō)明,ObjectBuilder Application Block提供以下的功能。
- 允許要求一個(gè)抽象對(duì)象或接口,ObjectBuilder會(huì)依據(jù)程序或組態(tài)文件的設(shè)定,傳回一個(gè)實(shí)體對(duì)象。
- 回傳一個(gè)既存對(duì)象,或是每次回傳一個(gè)新的對(duì)象(多半用于Dependency、Singleton情況,稍后會(huì)有詳細(xì)說(shuō)明)。
- 透過(guò)特定的Factory建立一個(gè)對(duì)象,這個(gè)Factory可以依據(jù)組態(tài)文件的設(shè)定來(lái)建立對(duì)象(CustomFactory,隸屬于Enterprise Common Library)。
- 當(dāng)物件擁有一個(gè)以上的構(gòu)造函數(shù)時(shí),依據(jù)已有的參數(shù),自動(dòng)選取兼容的構(gòu)造函數(shù)來(lái)建立要求的對(duì)象。(Consturctor Injection)
- 允許對(duì)象于建立后,透過(guò)程序或組態(tài)文件來(lái)賦值至屬性,或是調(diào)用特定的方法。(Setter Injection、Interface Injection)
- 提供一組Attribute,讓設(shè)計(jì)師可以指定需要Injection的屬性,亦或是于對(duì)象建立后需要調(diào)用的方法,也就是使用Reflection來(lái)自動(dòng)完成Injection動(dòng)作。
- 提供IBuilerAware接口,實(shí)現(xiàn)此接口的對(duì)象,ObjectBuilder會(huì)于建立該對(duì)象后,調(diào)用OnBuildUp或是OnTearDown方法。
- 提供TearDown機(jī)制,按建立對(duì)象的流程,反向釋放對(duì)象。
對(duì)于多數(shù)讀者來(lái)說(shuō),這些官方說(shuō)明相當(dāng)?shù)碾[誨,本文嘗試由架構(gòu)角度切入,討論ObjectBuidler的主要核心概念,再透過(guò)實(shí)現(xiàn)讓讀者們了解,該如何使用ObjectBuidler。
3-1、The Architecture of Object Builder
圖2
圖2是ObjectBuilder中四個(gè)主要核心對(duì)象的示意圖,BuidlerContext是一個(gè)概念型的環(huán)境對(duì)象,在這個(gè)對(duì)象中,包含著一組Strategys對(duì)象,一組Polices對(duì)象,一個(gè)Locator對(duì)象, ObjectBuidler采用Strategys Pipeline(策略流)概念,設(shè)計(jì)師必須透過(guò)Strategy串行來(lái)建立對(duì)象,而Strategy會(huì)透過(guò)Polices來(lái)尋找『類(lèi)型/id』對(duì)應(yīng)的Policy對(duì)象,使用 它來(lái)協(xié)助建立指定的對(duì)象。此處有一個(gè)必須特別提出來(lái)討論的概念,Strategy在架構(gòu)上是與類(lèi)型無(wú)關(guān)的,每個(gè)BuidlerContext會(huì)擁有一群Strategys對(duì)象,我們透過(guò)這個(gè)Strategys對(duì)象來(lái)建立任何類(lèi)型的對(duì)象,不管建立的對(duì)象類(lèi)型為何,都會(huì)通過(guò)這個(gè)Strategys Pipeline。這意味著,當(dāng)我們希望于建立A類(lèi)型對(duì)象后調(diào)用方法A1,于建立B類(lèi)型對(duì)象后調(diào)用方法 B1時(shí),負(fù)責(zé)調(diào)用方法的Strategy對(duì)象會(huì)需要一個(gè)機(jī)制來(lái)判別該調(diào)用那個(gè)方法,那就是Policy對(duì)象,BuilderContext中擁有一個(gè)Polices對(duì)象,其中存放著與『類(lèi)型/id』對(duì)應(yīng)的Policy對(duì)象,如圖3所示。
圖3
值得一提的是,Policy是以Type/id方式,也就是『類(lèi)型/id』方式來(lái)存放,這種做法不只可以讓不同類(lèi)型擁有各自的Policy,也允許同類(lèi)型但不同id擁有各自的Policy。ObjectBuilder中的最后一個(gè)元素是Locator,Locator對(duì)象在ObjectBuidler中扮演著前述的Service Locator角色,設(shè)計(jì)師可以用Key/Value的方式,將對(duì)象推入Locator中,稍后再以Key值來(lái)取出使用。
3-2、Strategys
ObjectBuilder內(nèi)建了許多Strategy,這些Strategy可以大略分成四種類(lèi)型,如圖4。
圖4
- Pre-Creation Strategy
Pre-Creation意指對(duì)象被建立前的初始動(dòng)作,參與此階段的Strategy有:TypeMappingStrategy、PropertyReflectionStrategy、ConstructorReflectionStrategy、MethodReflectionStrategy及SingletonStrategy,稍后我們會(huì)一一檢視 它們。
- Creation Strategy
Creation類(lèi)型的Strategy主要工作在于建立對(duì)象,它會(huì)利用Pre-Creation Strategys所準(zhǔn)備的參數(shù)來(lái)建立對(duì)象,ObjectBuilder就是運(yùn)用Pre-Creation的ConstructorReflectionStrategy及CreationStrategy來(lái)完成Constructor Injection動(dòng)作。
- Initialization Strategy
當(dāng)對(duì)象建立后,會(huì)進(jìn)入初始化階段,這就是Initialization Strategy階段,在此階段中,PropertySetterStrategy會(huì)與PropertyReflectionStrategy合作,完成Setter Injection。而MethodExecutionStrategy則會(huì)與MethodReflectionStrategy合作,在對(duì)象建立后,調(diào)用特定的方法,也就是Method Injection(視使用方式,Interface Injection是以此種方式完成的)。
- Post-Initialization Strategy
在對(duì)象建立并完成初始化動(dòng)作后,就進(jìn)入了Post-Initialization Strategy階段,在此階段中,BuilderAwareStrategy會(huì)探詢(xún)已建立的對(duì)象是否實(shí)現(xiàn)了IBuilderAware接口,是的話(huà)就調(diào)用IBuilderAware.OnBuildUp方法。
- 關(guān)于對(duì)象釋放
先前曾經(jīng)提過(guò),ObjectBuidler在建立對(duì)象時(shí),會(huì)一一調(diào)用所有Strategy來(lái)建立對(duì)象,同樣的!當(dāng)釋放對(duì)象時(shí),ObjectBuilder也會(huì)進(jìn)行同樣的動(dòng)作,不過(guò)方向是相反的,在內(nèi)建的Strategy中,只有BuilderAwareStrategy會(huì)參與對(duì)象釋放的動(dòng)作,在對(duì)象釋放時(shí),BuilderAwareStrategy會(huì)探詢(xún)欲釋放的對(duì)象是否實(shí)現(xiàn)了IBuidlerAware接口,是的話(huà)就調(diào)用IBuidlerAware.OnTearDown方法。
3-3、A Simple Application
再怎么詳細(xì)的說(shuō)明,少了一個(gè)實(shí)例就很難讓人理解,本節(jié)以一個(gè)簡(jiǎn)單的ObjectBuidler應(yīng)用實(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è)相當(dāng)陽(yáng)春的例子,在程序一開(kāi)始時(shí)建立了一個(gè)Builder對(duì)象,它是ObjectBuilder所提供的Facade對(duì)象,其會(huì)預(yù)先建立一般常用的Strategy串行,并于BuilderUp方法被調(diào)用時(shí),建立一個(gè)BuilderContext對(duì)象,并將Srategy串行及Polices串行指定給該BuilderContext,然后進(jìn)行對(duì)象的建立工作。
- How Object Creating
要了解前面的例子中,TestObject對(duì)象究竟是如何被建立起來(lái)的,首先必須深入Builder對(duì)象的建構(gòu)動(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);
}
當(dāng)Buidler對(duì)象被建立時(shí),其構(gòu)造函數(shù)會(huì)將前面所提及的幾個(gè)Strategys加到Strategies這個(gè)StrategyList Collection對(duì)象中,待BuildUp方法被調(diào)用時(shí)指定給新建立的BuilderContext對(duì)象。
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);
}
當(dāng)Builder的泛型方法BuildUp方法被調(diào)用后,其會(huì)調(diào)用非泛型的BuildUp方法,該方法會(huì)調(diào)用DoBuildUp方法,此處會(huì)透過(guò)strategies(先前于Builder構(gòu)造函數(shù)時(shí)初始化的StrategyList對(duì)象)來(lái)取得Strategys串行,并指定給稍后由MakeContext方法建立的BuilderContext,最后調(diào)用Strategy串行中第一個(gè)Strategy的BuildUp方法來(lái)進(jìn)行對(duì)象的建立動(dòng)作。在這一連串的動(dòng)作中,我們可以厘清幾個(gè)容易令人混淆的設(shè)計(jì),第一!我們是透過(guò)Strategy串行,也就是IBuidlerStrategyChain.Head.BuildUp來(lái)建立對(duì)象,這個(gè)Head屬性就是Strategy串行中的第一個(gè)Strategy。第二!BuilderContext的作用在于,于調(diào)用各個(gè)Strategy.BuildUp方法時(shí),給予 它們存取此次建立動(dòng)作所使用的Strategys及Policies等對(duì)象的機(jī)會(huì)。
- Policy物件的用途
現(xiàn)在,我們弄清楚了Strategy的用途,BuilderContext的真正涵意,但還有兩個(gè)元素尚未厘清,其中之一就是Policy對(duì)象,前面曾經(jīng)稍微提過(guò),Strategy是與類(lèi)型無(wú)關(guān)的設(shè)計(jì)概念,因此為了針對(duì)不同類(lèi)型做個(gè)別的處理,我們需要另一個(gè)與類(lèi)型相關(guān)的設(shè)計(jì),那就是Policy對(duì)象,要確認(rèn)這點(diǎn),必須重返Builder的構(gòu)造函數(shù)。
public Builder(IBuilderConfigurator<BuilderStage> configurator){
??? ..................
??? Policies.SetDefault<ICreationPolicy>(new DefaultCreationPolicy());
??? .................
}
這里調(diào)用了Policies的SetDefault方法,Policies是一個(gè)PolicyList對(duì)象,其提供了推入(Set、SetDefault)及取出(Get)方法,允許設(shè)計(jì)者針對(duì)所有『類(lèi)型/id』及特定『類(lèi)型/id』指定對(duì)應(yīng)的IBuilderPolicy對(duì)象,那這有什么用呢?這個(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于建立對(duì)象時(shí),會(huì)由Policies中取出『類(lèi)型/id』對(duì)應(yīng)的ICreationPolicy對(duì)象,接著利用 它來(lái)取得ConstructorInfo(構(gòu)造函數(shù)方法),再以GetParameters方法來(lái)取得構(gòu)造函數(shù)所需的參數(shù),最后調(diào)用此構(gòu)造函數(shù)。這段程序代碼告訴我們Policy的真正用途,就是用來(lái)協(xié)助Strategy于不同『類(lèi)型/id』對(duì)象建立時(shí),采取不同的動(dòng)作,這也就是說(shuō),Strategy與Policy通常是成對(duì)出現(xiàn)的。
- Locator
最后一個(gè)尚未弄清楚的關(guān)鍵元素是Locator,我們于調(diào)用Builder的BuildUp方法時(shí),建立了一個(gè)Locator對(duì)象并傳入該方法,這是用來(lái)做什么的呢?在ObjectBuilder中,Locator扮演兩種角色,第一個(gè)角色是提供一個(gè)對(duì)象容器供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è)對(duì)象只能有一份實(shí)體存在,當(dāng)此Strategy被喚起時(shí),其會(huì)先至Locator尋找目前要求的對(duì)象是否已被建立,是的話(huà)就取出該對(duì)象并傳回。Locator同時(shí)也可以作為一個(gè)Service Locator,這點(diǎn)可以由以下程序代碼來(lái)驗(yàn)證。
locator.Add("Test",new TestObject());.............
TestObject obj = locator.Get<TestObject>("Test");
當(dāng)然,這種手法有一個(gè)問(wèn)題,那就是TestObject對(duì)象是預(yù)先建立后放在Locator中,這并不是一個(gè)好的設(shè)計(jì),后面的章節(jié)我們會(huì)提出將Service Locator與Dependency Injection整合的手法。
PS:ObjectBuidler的Locator離完善的Service Locator還有段距離。
四、Dependency Injection With ObjectBuilder
ObjectBuilder支持Dependency Injection中定義的三種Injection模式,本章將一一介紹如何運(yùn)用ObjectBuilder來(lái)實(shí)現(xiàn)。
4-1、Constructor Injection
Constructor Injection的精神在于使用構(gòu)造函數(shù)來(lái)進(jìn)行注入動(dòng)作,本節(jié)延用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對(duì)象,會(huì)自行建立BuilderContext對(duì)象而不使用Builder對(duì)象的目的很單純,就是為了厘清個(gè)別Strategy究竟做了那些事,這點(diǎn)在使用Builder對(duì)象時(shí),會(huì)因?yàn)閮?nèi)建的Strategy都已加入,而顯得有些模糊。在MyBuilderContext對(duì)象建立后,此處將一個(gè)CreationStrategy加到Strategy串行中,CreationStrategy這個(gè)Strategy被歸類(lèi)為Creation階段,是真正建立對(duì)象的Strategy,緊接著UseValueParameter方法會(huì)被調(diào)用,這個(gè)方法中建立了一個(gè)ConstructorPolicy對(duì)象,并調(diào)用其AddParameter方法,加入一個(gè)ValueParameter對(duì)象,這個(gè)ValueParameter對(duì)象就對(duì)應(yīng)著InputAccept的構(gòu)造函數(shù)所需的參數(shù),CreationStrategy于對(duì)象建立后,會(huì)透過(guò)BuilderContext的Policies來(lái)取得『類(lèi)型/id』對(duì)應(yīng)的ICreationPolicy對(duì)象(本例就是ConstructorPolicy對(duì)象),然后調(diào)用ICreationPolicy.SelectionConstructor方法,這個(gè)方法必須根據(jù)調(diào)用者已用ICreationPolicy.AddParameter所傳入的參數(shù)來(lái)選擇正確的構(gòu)造函數(shù),然后再調(diào)用這個(gè)構(gòu)造函數(shù)并填入?yún)?shù)值來(lái)完成對(duì)象建立工作,圖5是這整個(gè)流程的示意圖。
圖5
圖中讀者可能會(huì)有所迷惑的是,FormatterServices.GetSafeUninitializedObject方法是何作用?這是.NET Framework中一個(gè)建立對(duì)象的途徑,與一般new或是Activator.CreateInstance方式不同,GetSafeUninitializedObject方法并不會(huì)觸發(fā)該對(duì)象的構(gòu)造函數(shù),只是單純的將對(duì)象建立起來(lái)而已,因此CreationStrategy才必須于最后調(diào)用對(duì)應(yīng)的構(gòu)造函數(shù)。
- Understanding Parameter
程序11中使用了一個(gè)ValueParameter對(duì)象,要知道這個(gè)對(duì)象的作用,我們得先了解Parameter在ObjectBuilder中所代表的意義,在三種注入模式中,有一個(gè)共通的規(guī)則,就是需要有參數(shù)來(lái)注入,Constructor Injection是透過(guò)構(gòu)造函數(shù)參數(shù)注入,而Interface Injection則是透過(guò)函數(shù)參數(shù)注入,Setter Injection則是透過(guò)屬性注入,因此參數(shù)是這三種注入模式都會(huì)用到的觀念,所以O(shè)bjectBuilder定義了IParameter接口,并提供一組實(shí)現(xiàn)此接口的參數(shù)對(duì)象,于注入時(shí)期由這些參數(shù)對(duì)象來(lái)取得參數(shù)值,如圖6。
圖6
- ValueParameter
這是一個(gè)最簡(jiǎn)單的Paramter對(duì)象,構(gòu)造函數(shù)如下所示:
public ValueParameter(Type valueType, object value)它的GetValue方法僅是將構(gòu)造函數(shù)傳入的value對(duì)象傳回而已。
- DependencyParamter
DependencyParameter是一個(gè)功能強(qiáng)大的Parameter對(duì)象,程序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ā)現(xiàn),DependencyParameter并未要求建構(gòu)者傳入任何對(duì)象實(shí)體,而是要求建構(gòu)者傳入注入時(shí)對(duì)應(yīng)的參數(shù)類(lèi)型、參數(shù)名稱(chēng)、實(shí)體類(lèi)型、NotPersentBehavoir及SearchMode等參數(shù),下面的程序行表是DependencyParameter的構(gòu)造函數(shù):
public DependencyParameter(Type parameterType, string name,????????? Type createType, NotPresentBehavior notPresentBehavior, SearchMode searchMode)
第一個(gè)參數(shù)是參數(shù)的類(lèi)型,第二個(gè)參數(shù)是參數(shù)的名稱(chēng),當(dāng)ConstructorPolicy于SelectConstructor方法時(shí),會(huì)依據(jù)這兩個(gè)參數(shù)來(lái)選取適合的構(gòu)造函數(shù),第三個(gè)參數(shù)是實(shí)體對(duì)象的類(lèi)型,以本例來(lái)說(shuō),就是以PromptDataProcessor這個(gè)類(lèi)型建立對(duì)象來(lái)傳入需要IDataProcessor類(lèi)型的構(gòu)造函數(shù)、方法或?qū)傩?#xff0c;第四個(gè)參數(shù)則影響了DependencyParameter的取值動(dòng)作,預(yù)設(shè)情況下,DependencyParameter會(huì)先至Locator中取值,這個(gè)動(dòng)作會(huì)受到第五個(gè)參數(shù):SearchMode的影響(稍后會(huì)介紹這一部份),如果找不到的話(huà),就會(huì)依據(jù)此參數(shù)值來(lái)做動(dòng)作,NotPersentBehavior這個(gè)列舉的定義如下:
public enum NotPresentBehavior{???????????????????
?? CreateNew,
?? ReturnNull,
?? Throw,
}
CreateNew代表著當(dāng)DependencyParameter于Locator找不到需要的值時(shí),調(diào)用BuilderContext.HeadOfChain.BuildUp方法來(lái)建立該對(duì)象,以此例來(lái)說(shuō)即是如此,所建立對(duì)象的類(lèi)型就是PromptDataProcessor。ReturnNull則是回傳一個(gè)Null值,Throw則是直接拋出一個(gè)例外。好了,了解了整體流程后,現(xiàn)在讓我們一一厘清這個(gè)流程中剩下的部份,第一!于Locator找尋需要的值是什么意思,試想一種情況,當(dāng)我們?cè)谧鯠ependency Injection時(shí),是否有某些欲注入對(duì)象是可重用的,也就是該對(duì)象可以只建立一個(gè),注入多個(gè)不同的對(duì)象,讓這些對(duì)象共享這個(gè)注入對(duì)象,這就是DependencyParameter會(huì)先至Locator中找尋已推入的注入對(duì)象的原因,請(qǐng)參考程序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è)例子預(yù)先建立了一個(gè)PromptDataProcessor對(duì)象,并以DependencyResolutionLocatorKey封裝后推入Locator中,這樣一來(lái),當(dāng)DependencyParameter取值時(shí),就會(huì)依據(jù)參數(shù)的『類(lèi)型/id』至Locator找尋需要的值,此時(shí)就會(huì)得到我們所推入的PromptDataProcessor對(duì)象,而不是建立一個(gè)新的,另外!只要于AddParameter所傳入的DependencyParameter是以IDataProcessor為參數(shù)類(lèi)型,并以null為id(名稱(chēng))的話(huà),那么永遠(yuǎn)都會(huì)傳回我們所推入Locator的PromptDataProcessor 對(duì)象。第二個(gè)要厘清的是SearchMode的涵意,在ObjectBuilder的架構(gòu)上,Locator是可以有Parent/Child關(guān)系的,當(dāng)DependencyParameter要找尋需要的對(duì)象時(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)的,當(dāng)DependencyParameter無(wú)法于Locator找到需要的對(duì)象而透過(guò)BuildUp來(lái)建立對(duì)象時(shí),該『類(lèi)型/id』同樣需要一個(gè)ICreationPolicy來(lái)對(duì)應(yīng),否則將會(huì)引發(fā)Missing Policy的例外,注意!DependencyParameter所使用的name參數(shù)必須與設(shè)定Set<ICreationPolicy>時(shí)所傳入的第三個(gè)參數(shù)相同。最后一個(gè)問(wèn)題是,如果每個(gè)『類(lèi)型/id』都要設(shè)定對(duì)應(yīng)的ICreationPolicy,豈不累人,ObjectBuilder當(dāng)然沒(méi)有這么不人性化,我們可以調(diào)用Policies.SetDefault來(lái)為所有『類(lèi)型/id』預(yù)設(shè)一個(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());
}
- CreationParameter
與DependencyParameter相同,CreationParameter也會(huì)透過(guò)BuildUp來(lái)建立對(duì)象,不同的是其不會(huì)先搜尋Locator,也無(wú)法作參數(shù)類(lèi)型與實(shí)體類(lèi)型對(duì)應(yīng),因此無(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
CloneParameter的構(gòu)造函數(shù)接受一個(gè)IParameter參數(shù),當(dāng)其GetValue方法被調(diào)用時(shí),會(huì)透過(guò)從構(gòu)造函數(shù)指定的Parameter對(duì)象來(lái)取值,如果取得的值是實(shí)現(xiàn)了ICloneable接口的對(duì)象時(shí),其將調(diào)用Clone方法來(lái)拷貝該值,否則傳回原值,下面的程序片斷是CloneParametr的構(gòu)造函數(shù)聲明。
public CloneParameter(IParameter param)- LookupParameter
LookupParameter的構(gòu)造函數(shù)接受一個(gè)object類(lèi)型的參數(shù),當(dāng)GetValue方法被調(diào)用時(shí),會(huì)經(jīng)由Locator.Get方法,以構(gòu)造函數(shù)所傳入的參數(shù)為鍵值,取得位于Locator中的值,下面的程序片斷為L(zhǎng)ookupParameter的構(gòu)造函數(shù)聲明。
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對(duì)象來(lái)進(jìn)行Consturctor Injection時(shí),設(shè)計(jì)者必須在建立對(duì)象前,預(yù)先準(zhǔn)備這些Parameter對(duì)象,雖然動(dòng)作不算繁鎖,但若全部對(duì)象的建立都要這么做,未免有些沒(méi)有效率,為此!ObjectBuilder提供了另一種較為簡(jiǎn)單的方法,就是利用InjectionConstructor這個(gè)Attribute,再搭配上ConstructorReflectionStrategy對(duì)象,自動(dòng)的為設(shè)計(jì)者準(zhǔn)備這些Parmeter對(duì)象,程序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對(duì)象, 它會(huì)于建立對(duì)象動(dòng)作時(shí),探詢(xún)欲建立對(duì)象類(lèi)型所提供的所有構(gòu)造函數(shù),選取已標(biāo)上InjectionConstrucor Attribute的那個(gè)為指定構(gòu)造函數(shù),接著ConstructorReflectionStrategy會(huì)探詢(xún)?cè)摌?gòu)造函數(shù)的所有參數(shù),查看是否標(biāo)上Dependency Attribute,是的話(huà)就以其設(shè)定建立DependencyParameter,否則建立一個(gè)新的DependencyParameter,它會(huì)單以類(lèi)型參數(shù)來(lái)建立DependencyParameter,最后ConstructorReflectionStrategy會(huì)以這些信息來(lái)建立對(duì)應(yīng)的ConstructorPolicy對(duì)象,完成整個(gè)對(duì)象建立動(dòng)作。
- Understanding Dependency Attribute
ConstructorReflectionStrategy依賴(lài)兩個(gè)關(guān)鍵的Attribute,一個(gè)是用來(lái)標(biāo)示指定構(gòu)造函數(shù)的InjectionConstructor Attribute,另一個(gè)則是用來(lái)標(biāo)示參數(shù)該如何取得的Dependency Attribute,此Attribute有四個(gè)屬性,分別對(duì)應(yīng)到DependencyParameter的四個(gè)屬性:
| DependencyAttribute | DependencyParameter | 說(shuō)明 |
| Name | Name | id(名稱(chēng)) |
| CreateType | CreateType | 欲建立對(duì)象的實(shí)體類(lèi)型 |
| NotPersentBehavior | NotPersentBehavior | 當(dāng)欲建立對(duì)象無(wú)法由Locator取得時(shí)的行為模式。 |
| SearchMode | SearchMode | 對(duì)Locator的搜尋法則。 |
使用Dependency Attribute與ConsturctorReflectionStrategy模式的優(yōu)點(diǎn)是設(shè)計(jì)者不需花費(fèi)時(shí)間一一建立Parameter對(duì)象,而缺點(diǎn)就是CreateType參數(shù),由于ConstructorReflectionStrategy依賴(lài)著Dependency Attribute的CreateType參數(shù)來(lái)決定實(shí)際建立對(duì)象的類(lèi)型,這使得設(shè)計(jì)者必須在標(biāo)示Dependency Attribute時(shí),一并指定這個(gè)參數(shù),否則ConstructorReflectionStrategy將會(huì)以參數(shù)類(lèi)型做為建立實(shí)際對(duì)象時(shí)的類(lèi)型,而在本例中,我們無(wú)法建立一個(gè)IDataProcessor對(duì)象,這點(diǎn)降低了程序的可訂制性。那這要如何解決呢?簡(jiǎn)單的方法是撰寫(xiě)一個(gè)新的Dependency Attribute、或是使用TypeMappingStrategy,復(fù)雜的則是撰寫(xiě)一個(gè)新的ConstructorReflectionStrategy,后面的章節(jié)我們會(huì)再重訪這個(gè)問(wèn)題。
- Injection with DependencyResolutionLocator
前面談到DependencyParameter時(shí)曾經(jīng)提過(guò),它會(huì)先至Locator中搜尋需要的參數(shù)值,那么這也意味著,在使用ConstructorReflectionStrategy時(shí),我們可以將參數(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)
{
??? ...................
}
當(dāng)然,這仍然會(huì)有一個(gè)問(wèn)題,那就是必須預(yù)先建立PromptDataProcessor對(duì)象,而非于InputAccept對(duì)象建立時(shí)期建立,這是在不撰寫(xiě)自定Dependency Attribute或Strategy,亦或是使用TypeMappingStrategy情況下的簡(jiǎn)易解法。
- DefaultCreationPolicy and ConstructorPolicy
ObjectBuilder內(nèi)建了兩個(gè)ICreationPolicy的實(shí)現(xiàn)對(duì)象,一是前面所使用的ConstructorPolicy,二是DefaultCreationPolicy,與ConstructorPolicy不同,DefaultCreationPolicy永遠(yuǎn)使用預(yù)設(shè)的構(gòu)造函數(shù),如下所示。
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());
}
而調(diào)用該構(gòu)造函數(shù)時(shí)所需的參數(shù),則直接以BuildUp方法,依據(jù)參數(shù)的『類(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ì)選擇頂端的構(gòu)造函數(shù),二是其一律以BuidUp方法依據(jù)參數(shù)類(lèi)型來(lái)建立參數(shù)對(duì)象,不需要設(shè)計(jì)者介入。那在何種情況下選擇DefaultCreationPolicy呢?一般來(lái)說(shuō),使用ConstructorPolicy時(shí),因?yàn)槠鋾?huì)依據(jù)設(shè)計(jì)者所加入的Parameter對(duì)象來(lái)選擇構(gòu)造函數(shù),如果設(shè)計(jì)者未準(zhǔn)備這些,那么ConstructorPolicy將因無(wú)法取得適合的構(gòu)造函數(shù)而引發(fā)例外,雖然這點(diǎn)可以經(jīng)由搭配ConstructorReflectionStrategy來(lái)解決,但使用ConstructorReflectionStrategy時(shí)必須搭配Dependency Attribtue及InjectionConstructor Attribute,所以也是個(gè)負(fù)擔(dān)。使用DefaultCreationPolicy就沒(méi)有這些問(wèn)題了,缺點(diǎn)則是無(wú)法指定實(shí)際建立的參數(shù)對(duì)象類(lèi)型,所以DefautlCreationPolicy通常被設(shè)定成預(yù)設(shè)的ICreationPolicy,主要作用在于當(dāng)我們所建立的對(duì)象是簡(jiǎn)單的,只有一個(gè)構(gòu)造函數(shù),且不需要特別指定參數(shù)實(shí)際類(lèi)型時(shí),就交由它來(lái)處理,而需要特別處理的,就運(yùn)用『類(lèi)型/id』對(duì)應(yīng)的ConstructorPolicy或是Dependency Attribute、Injection Constructor Attrbute搭配ConstructorReflectionStrategy來(lái)處理。
4-2、Interface Injection
Interface Injection在ObjectBuidler中可以經(jīng)由Method Injection來(lái)完成,指的是在對(duì)象建立后,調(diào)用所指定的方法來(lái)完成初始化動(dòng)作,而負(fù)責(zé)這個(gè)工作的就是MethodExecutionStrategy,本節(jié)持續(xù)延應(yīng)InputAccept來(lái)示范如何于ObjectBuidler中實(shí)現(xiàn)Interface Injection。
- MethodExecutionStrategy
要實(shí)現(xiàn)Interface Injection,除了必須使用CreationStrategy來(lái)建立對(duì)象外,還要使用另一個(gè)Strategy:MethodExecutionStrategy, 它會(huì)在對(duì)象建立完成后,執(zhí)行指定的方法,程序19是使用MethodExecutionStrategy來(lái)實(shí)現(xiàn)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)行調(diào)用指定方法時(shí)的參數(shù)注入動(dòng)作,在使用MethodExecutionStrategy時(shí),設(shè)計(jì)者必須先行建立調(diào)用方法時(shí)所需的MethodCallInfo對(duì)象,這是一個(gè)實(shí)現(xiàn)IMethodInfo接口的對(duì)象,設(shè)計(jì)者必須于此對(duì)象中指定欲調(diào)用的方法、及傳入的Parameter對(duì)象,下面是MethodInfo的構(gòu)造函數(shù)聲明。
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擁有許多重載的構(gòu)造函數(shù),大概分成兩大類(lèi):方法名稱(chēng)及MethodInfo對(duì)象,每類(lèi)會(huì)分成四個(gè),分別是無(wú)參數(shù)、使用params傳入?yún)?shù)值、使用params傳入IParamete對(duì)象、傳入IEnumerable<IParameter>對(duì)象。在MethodInfo對(duì)象建立后,接著就要將這些對(duì)象傳入IMethodPolicy對(duì)象,并指定給context.Policies對(duì)象,這樣就完成了Interface Injection的準(zhǔn)備動(dòng)作,之后建立InputAccept對(duì)象后,SetDataProcess方法就會(huì)被調(diào)用,同時(shí)會(huì)傳入指定的PromptDataProcessor對(duì)象。
- How MethodExecutionStrategy Working?
當(dāng)MethodExecutionStrategy的BuildUp方法被調(diào)用時(shí),會(huì)透過(guò)context.Policies來(lái)取得類(lèi)型對(duì)應(yīng)的IMethodPolicy對(duì)象,如下所示。
IMethodPolicy policy = context.Policies.Get<IMethodPolicy>(type, id);然后會(huì)透過(guò)IMethodPolicy對(duì)象來(lái)取得所有需要處理的IMethodCallInfo對(duì)象,并一一調(diào)用其SelectMethod方法來(lái)取得欲調(diào)用方法,如下所示。
MethodInfo methodInfo = methodCallInfo.SelectMethod(context, type, id);SelectMethod方法會(huì)依據(jù)當(dāng)初建立此IMethodCallInfo對(duì)象時(shí)所指定的方法名稱(chēng)、參數(shù)數(shù)量及類(lèi)型來(lái)取得對(duì)應(yīng)方法的MethodInfo對(duì)象。于取得MethodInfo對(duì)象后,緊接著就是透過(guò)IMethodCallInfo.GetParameters方法來(lái)取得調(diào)用此方法時(shí)需傳入的參數(shù)值,如下所示。
object[] parameters = methodCallInfo.GetParameters(context, type, id, methodInfo);最后調(diào)用MethodInfo.Invoke方法來(lái)調(diào)用該方法就完成整個(gè)動(dòng)作了。
methodInfo.Invoke(obj, parameters);好了,這就是MethodExecutionStrategy的整個(gè)流程,現(xiàn)在我們要厘清幾個(gè)可能會(huì)令人困惑的問(wèn)題,第一!當(dāng)欲調(diào)用的方法是重載,有多個(gè)同名方法時(shí),SelectMethod依據(jù)什么來(lái)決定要調(diào)用那一個(gè)?答案是參數(shù)數(shù)量及類(lèi)型。第二!當(dāng)使用Parameter對(duì)象傳入MethodCallInfo對(duì)象的構(gòu)造函數(shù)時(shí),GetParameters方法會(huì)透過(guò)Parameter.GetValue來(lái)取值,那么當(dāng)直接以object[]方式傳入MethodCallInfo的構(gòu)造函數(shù)時(shí)呢?答案是該構(gòu)造函數(shù)會(huì)逐個(gè)為傳入的object建立ValueParameter對(duì)象,如下所示。
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è)以上的函數(shù)調(diào)用嗎?答案是可以,建立對(duì)應(yīng)的MethodCallInfo對(duì)象,并加到IMethodPolicy后即可,調(diào)用的順序則是依照MethodCallInfo加入IMethodPolicy的順序。
- Use DependencyParameter
與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對(duì)象,要使用它,我們必須為欲進(jìn)行Method Injection的方法標(biāo)上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ō),相信讀者對(duì)這種模式已經(jīng)了然于胸了。
- Injection with Dependency Attribute and CreateType
同樣的,我們也可以在Dependency Attribute中指定CreateType來(lái)達(dá)到同樣的效果,如程序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);
??? }
}
設(shè)計(jì)者必須預(yù)先建立PropertySetterInfo對(duì)象,并為其指定欲設(shè)定的屬性名稱(chēng)及參數(shù),PropertySetterInfo是一個(gè)實(shí)現(xiàn)了IPropertySetterInfo接口的對(duì)象,其構(gòu)造函數(shù)聲明如下。
public PropertySetterInfo(string name, IParameter value)public PropertySetterInfo(PropertyInfo propInfo, IParameter value)
有了MethodCallInfo的經(jīng)驗(yàn),讀者們對(duì)這些構(gòu)造函數(shù)應(yīng)該不會(huì)有任何疑惑,應(yīng)該會(huì)抱怨其不像MethodCallInfo般提供那么多的選擇吧(笑)。在ProeprtySetterInfo建立后,接著只要將其加到IPropertySetterPolicy對(duì)象中,并依『類(lèi)型/id』指定給context.Policies即可完成Setter Injection。
- How PropertySetterStrategy Work?
當(dāng)PropertySetterStrategy的BuildUp方法被調(diào)用時(shí),會(huì)透過(guò)context.Policies來(lái)取得類(lèi)型對(duì)應(yīng)的IPropertySetterPolicy對(duì)象,如下所示。
IPropertySetterPolicy policy = context.Policies.Get<IPropertySetterPolicy>(type, id);然后會(huì)透過(guò)IMethodPoliIPropertySetterPolicyy對(duì)象來(lái)取得所有需要處理的IPropertySetterInfo對(duì)象,并一一調(diào)用其SelectProperty方法來(lái)取得欲設(shè)定的屬性,如下所示。
PropertyInfo propInfo = propSetterInfo.SelectProperty(context, type, id);SelectProperty方法會(huì)依據(jù)當(dāng)初建立此IPropertySetterInfo對(duì)象時(shí)所指定的屬性名稱(chēng)、參數(shù)來(lái)取得對(duì)應(yīng)屬性的PropertyInfo對(duì)象。于取得PropertyInfo對(duì)象后,緊接著就是透過(guò)IPropertySetterInfo.GetValue方法來(lái)取得設(shè)定此屬性時(shí)需傳入的值,如下所示。
object value = propSetterInfo.GetValue(context, type, id, propInfo);最后調(diào)用PropertyInfo.SetValue方法來(lái)設(shè)定屬性值就完成整個(gè)動(dòng)作了。
propInfo.SetValue(obj, value, null);這就是整個(gè)Setter Injection的流程,這里只有一個(gè)問(wèn)題,我們可以設(shè)定一個(gè)以上的屬性嗎?答案是肯定的,只要建立對(duì)應(yīng)數(shù)量的PropertySetterInfo對(duì)象即可。
- use DependencyParameter
同樣的,使用DependencyParameter也可以達(dá)到同樣的效果,如程序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
相對(duì)于ConsturctorReflectionStrategy及MethodReflectionStrategy,ObjectBuilder也提供了一個(gè)同類(lèi)型的PropertyReflectionStrategy,我們可以搭配Dependency Attribute及DependencyResolutionLocatorKey對(duì)象來(lái)達(dá)到同樣效果,如程序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參數(shù)來(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;
??????? }
??? }
??? ...............
}
轉(zhuǎn)載于:https://www.cnblogs.com/sw22225458/archive/2008/06/12/1218695.html
總結(jié)
以上是生活随笔為你收集整理的Object Builder Application Block (2)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 分辨率设置640*480
- 下一篇: 记录AJAX在VS2005中的使用第二编