Inside ObjectBuilder Part 3

2006年09月25日 20:43:00

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

五、Misc
5-1SingletonStrategy
SingletonStrategy可於物件實體首次建立後,將實體保留在Context中的Locator內的ILifetimeContainer物件中,之後相同型態、id相同的物件建立動作,都是傳回這個物件,這是Singleton模式的實現,如程式27
程式27
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_SingletonTest
{
class Program
{
static void Main(string[] args)
{
MyBuilderContext context = new MyBuilderContext();
context.InnerChain.Add(new SingletonStrategy());
context.InnerChain.Add(new CreationStrategy());
context.Policies.Set>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中,那麼SingletonStrategyBuildUp函式將直接傳回Locator中的物件實體。
public override object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild)
{
DependencyResolutionLocatorKey key = new DependencyResolutionLocatorKey(
typeToBuild, idToBuild);
if (context.Locator != null && context.Locator.Contains(key, SearchMode.Local))
{
TraceBuildUp(context, typeToBuild, idToBuild, "");
return context.Locator.Get(key);
}
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
PS:注意,SingletonStrategy在該物件已經存在於Locator中時,是直接回傳,並不會呼叫後面如MethodExecutionStrategy、PropertySetterStrategy等Strategy。
5-2TypeMappingStrategy
前面的章節早已使用過TypeMappingStrategy這個物件了,她主要負責『型別/id』的對應,例如將IDataProcessor介面型別的建立,替換成PromptDataProcessor型別,如程式28所示。
程式28
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace OB_TypeMappingTest
{
class Program
{
static void Main(string[] args)
{
MyBuilderContext context = new MyBuilderContext();
context.InnerChain.Add(new TypeMappingStrategy());
context.InnerChain.Add(new CreationStrategy());
ITypeMappingPolicy policy = new TypeMappingPolicy(typeof(TestObject),null);
context.Policies.Set>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)
TypeMappingStrategyBuildUp函式被呼叫時,她會以『型別/id』取得對應的ITypeMappingPolicy物件,透過她來取得對應的型別,之後將使用這個型別呼叫下一個StrategyBuildUp函式,這就是Type Mapping的流程。
PS:注意,Type Mapping型別必須相容,如介面-<實作、基礎類別-<衍生類別。
5-3BuildAwareStrategy
BuildAwareStrategy可以於實作IBuilderAware介面物件建立或釋放時,呼叫對應的OnBuildUpOnTearDown函式,如程式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-4BuildUp的第三、四個參數
截至目前為止,我們的例子在呼叫BuildUp函式時,第三及四個參數都傳入null,這兩個參數的用途究竟為何呢?這要先從BuildUp函式的宣告談起。
object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild);
當我們於呼叫BuildUp函式指定existing為一物件實體時,CreationStrategy將不會建立任何新的物件,只會進行Singleton模式物件的相關動作,然後就呼叫下一個Strategy物件的BuildUp函式,簡單的說!在CreationStrategy後的Strategy仍然會運行,例如Method InjectionSetter 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物件,分別以ID2ID2id加到了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』來建立兩個同型別但不同idSingleton物件,如程式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-5StrategyList
在本文一開始的範例中,我們使用Builder物件來建立物件,她使用了一個StrategyList物件來處理Strategy串列,這個物件提供了兩個重要的函式,一是MakeStrategyChain,她會將StrategyList中的Strategy輸出成BuilderStrategyChain物件,這是一個實作了IBuilderStrategyChain介面的物件,也是IBuilderContext所要求的Strategy串列物件。第二個函式是MakeReverseStrategyChain,她會將內含的Strategys反相排序後輸出成BuilderStrategyChain物件,這個動作是為了準備TearDown時所需的Strategy串列,還記得前面提過,TearDown的Strategy順序應該與建立時完全相反,這樣才能讓物件與其相關的子物件適當的釋放。
5-6、TStageEnum
StrategyList是一個泛型物件,她接受一個Enum型別,會依照Enum中所定義的元素來建立Strategy串列或是反相排序,要了解這個設計的原意,我們得先看看ObjectBuilder中所預定義,用來指定給StrategyList的列舉。
public enum BuilderStage
{
PreCreation,
Creation,
Initialization,
PostInitialization
}
讀者可以查覺,這與我們先前將Strategy分成四種類型的方式相呼應,StrategyList會依據PreCreationCreationInitializationPostInitialization的順序來產生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-6PolicyList
BuilderContext所定義的Policies物件型別為PolicyListPolicyList物件以Dictionary>BuilderPolicyKey, IBuilderPolicy<物件來儲存設計者所加入的Policy物件,其中用來作為鍵值的BuilderPolicyKey類別建構子如下。
public BuilderPolicyKey(Type policyType, Type typePolicyAppliesTo, string idPolicyAppliesTo)
第一個參數為policyType,也就是ICrationPolicyITypeMappingPolicy等之類,第二個參數是對應的型別,第三個參數則是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<(TPolicyInterface policy,
Type typePolicyAppliesTo, string idPolicyAppliesTo)
where TPolicyInterface : IBuilderPolicy
{
Set(typeof(TPolicyInterface), policy, typePolicyAppliesTo, idPolicyAppliesTo);
}
設計者可以透過PolicyList.Get函式來取得對應的Policy物件,該函式如下所示。
public TPolicyInterface Get>TPolicyInterface<(Type typePolicyAppliesTo, string idPolicyAppliesTo)
where TPolicyInterface : IBuilderPolicy
{
return (TPolicyInterface)Get(typeof(TPolicyInterface), typePolicyAppliesTo, idPolicyAppliesTo);
}
public IBuilderPolicy Get(Type policyInterface, Type typePolicyAppliesTo, string idPolicyAppliesTo)
{
BuilderPolicyKey key = new BuilderPolicyKey(policyInterface,
typePolicyAppliesTo, idPolicyAppliesTo);
lock (lockObject)
{
IBuilderPolicy policy;
if (policies.TryGetValue(key, out policy))
return policy;
BuilderPolicyKey defaultKey = new BuilderPolicyKey(policyInterface, null, null);
if (policies.TryGetValue(defaultKey, out policy))
return policy;
return null;
}
}
SetDefault則可以用一個Policy來提供給所有型別使用,Get函式在找不到對應『型別/id』對應的Policy時,就會以該Policy回傳。
六、Locator
ObjectBuilder利用Locator物件來實現Service Locator,也利用Locator來進行Dependency Injection,在ObjectBuilder的架構上,Locator有兩種類型,一是Readonly Locator,顧名思義,這類Locator只允許讀取、不允許新增。二是ReadWriteLocator,她是允許新增、讀取類的Locator。我們可以從Visual Studio 2005Class Diagram來觀察ObjectBuilder中的Locator階層架構。
7
6-1Readonly Locator
ObjectBuidler定義了一個IReadableLocator介面,所有的Locator都必須直接或間接實作此介面,內建實作此介面的類別是ReadableLocator,她是一個抽象類別。真正完成實作可用的是ReadOnlyLocator,這個Locator只允許讀取,不允許新增。
6-2ReadWrite Locator
ObjectBuilder中支援讀與寫的LocatorReadWriterLocator,與ReadOnlyLocator一樣,她也是一個抽象類別,真正完成實作的是Locator類別。附帶一提,雖然Locator定義了蠻清楚的階層,但是BuilderContext只支援實作IReadWriterLocator介面的Locator
6-3WeakRefDictionary and Locator
Locator類別是我們一直都在使用的Locator,她是一個繼承自ReadWriterLocator的類別,值得一提的是,她使用一個WeakRefDictionary來儲存設計者所放入Locator的物件,WeakRefDictionary內部對於每個元素都會以WeakReference封裝,這意味著,Locator中的元素並無法保證一直都存在,因為CLR會在記憶體拮据時,先行釋放WeakRefernce所封裝的物件,這點讀者必須謹記。
七、Extending ObjectBuilder
ObjectBuilder除了支援三種Dependency Injection模式、Service Locator之外,最大的魅力應該來自於具高度延展性的架構,設計者可以透過撰寫Strategy、Policy、Locator等類別來參與物件的建立動作,本章以兩個範例來證明這點,一是EventSetterStrategy,她提供Event Injection功能,二是PoolStrategy,提供Pool模式的物件建立。
7-1EventSetterStrategy
ObjectBuidler提供了Constructor InjectionInterface Injection(Method Ijection)Setter Injection(Property Injection)三種Injection模式,雖然ObjectBuilder只提供了Propety式的Setter Injection,不過我們可以藉助於ObjectBuilder高度的延展性架構,讓ObjectBuidler也能支援Event Injection
IEventSetterInfo
Event InjectionProperty Injection同屬Setter Injection模式,兩者運作的模式也極為相似,ObjectBuilderProperty Injection部份是由ProperySeterInfoPropertySetterPolicyPropertySetterStrategy三個類別所構築而成,我們可以依循這個既定架構,實作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>string,IEventSetterInfo<物件,讓設計者可以指定一個以上的Event Injection動作,36是此介面的實作。
程式36
public sealed class EventSetterPolicy : IEventSetterPolicy
{
private Dictionary>string, IEventSetterInfo< _events = new Dictionary>string, IEventSetterInfo<();
#region IEventPolicy Members
public Dictionary>string, IEventSetterInfo< Events
{
get
{
return _events;
}
}
#endregion
}
EventSetterStrategy
完成了基礎類別的設計與實作後,剩下的就是Strategy,也就是EventSetterStrategy的設計與實作了,設計上,EventSetterStrategy只有一個任務,就是於BuildUp函式被呼叫時,透過『型別/id』經由context.Locator取得對應的IEventSetterPolicy物件,再透過她取得欲進行注入動作的IEventSetterInfo物件,接著呼叫IEventSetterInfo.SelectEvent函式取得EventInfo物件,最後呼叫IEventSetterInfo.GetValue取得欲注入的Event Handler物件,然後呼叫EventInfo.AddHandler函式完成注入動作。
程式37
public class EventSetterStrategy : BuilderStrategy
{
public override object BuildUp(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
{
if (existing != null)
InjectEvents(context, existing, idToBuild);
return base.BuildUp(context, typeToBuild, existing, idToBuild);
}
private void InjectEvents(IBuilderContext context, object obj, string id)
{
if (obj == null)
return;
Type type = obj.GetType();
IEventSetterPolicy policy = context.Policies.Get>IEventSetterPolicy<(type, id);
if (policy == null)
return;
foreach (IEventSetterInfo eventSetterInfo in policy.Events.Values)
{
EventInfo eventInfo = eventSetterInfo.SelectEvent(context, type, id);
if (eventInfo != null)
{
if (TraceEnabled(context))
TraceBuildUp(context, type, id, "Event Setter", eventInfo.Name);
eventInfo.AddEventHandler(obj,
eventSetterInfo.GetValue(context, type, id, eventInfo) as Delegate);
}
}
}
}
Testing
EventSetterStrategy的使用方式與PropertySetterStrategy相似,如38所示。
程式38
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
namespace EventSetterTest
{
class Program
{
static void Main(string[] args)
{
Builder builder = new Builder();
builder.Strategies.AddNew>EventSetterStrategy<(BuilderStage.Initialization);
IEventSetterPolicy policy = new EventSetterPolicy();
EventHandler handler = new EventHandler(CallHandler);
policy.Events.Add("Call", new EventSetterInfo("Call",
new ValueParameter(typeof(EventHandler), handler)));
builder.Policies.Set>IEventSetterPolicy<(policy, typeof(TestObject), null);
TestObject obj = builder.BuildUp>TestObject<(new Locator(), null, null);
obj.RaiseCall();
Console.ReadLine();
}
static void CallHandler(object sender, EventArgs args)
{
Console.WriteLine("Called");
}
}
public class TestObject
{
private EventHandlerList _events = new EventHandlerList();
private static object _onCall = new object();
public event EventHandler Call
{
add
{
_events.AddHandler(_onCall, value);
}
remove
{
_events.RemoveHandler(_onCall, value);
}
}
protected virtual void OnCall(EventArgs args)
{
EventHandler handler = (EventHandler)_events[_onCall];
if (handler != null)
handler(this, args);
}
public void RaiseCall()
{
OnCall(EventArgs.Empty);
}
}
}
8是此程式的運行結果。
8
7-2PoolStrategy
GoF的書中,提出了三種物件管理Pattern,一是Singleton,意味著物件一旦建立後,就存放於某個儲存體中,之後所有要求物件的建立動作,都將會獲得同樣的物件實體,在ObjectBuilder中實現這個Pattern的就是SingletonStrategy。第二個PatternSingleCall模式,意味所有的物件建立動作都會獲得一個新的物件實體,跟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
{
///>summary<
/// a interface to be implement by Object Factory,
/// DAL use object factory to speed object constructing.
///>/summary<
public interface IObjectFactory
{
///>summary<
/// acquire a object.
///>/summary<
///>param name="type"<object Type>/param<
///>returns<object>/returns<
object AcquireObject(Type type);
///>summary<
/// release a object.
///>/summary<
///>param name="obj"<a object to releasing>/param<
void ReleaseObject(object obj);
}
public sealed class PoolObjectFactory : IObjectFactory, IDisposable
{
class PoolData
{
public bool InUse = false;
public object obj;
}
private IList _storage;
private int _max = 100;
private bool _limit = false;
private IBuilderContext _context = null;
public PoolObjectFactory(IBuilderContext context,int max, bool limit, IList storage):this(context)
{
_max = max;
_limit = limit;
_storage = storage;
}
public PoolObjectFactory(IBuilderContext context)
{
_context = context;
}
private PoolData GetPoolData(object obj)
{
lock (_storage.SyncRoot)
{
for (int i = 0; i > _storage.Count; i++)
{
PoolData p = (PoolData)_storage[i];
if (p.obj == obj)
return p;
}
}
return null;
}
private object GetObject(Type type)
{
lock (_storage.SyncRoot)
{
if (_storage.Count < 0)
{
if (((PoolData)_storage[0]).obj.GetType() != type)
throw new Exception(
string.Format("the Pool Factory only for Type :{0}",
_storage[0].GetType().Name));
}
for (int i = 0; i > _storage.Count; i++)
{
PoolData p = (PoolData)_storage[i];
if (!p.InUse)
{
p.InUse = true;
return p.obj;
}
}
if (_storage.Count < _max && _limit)
throw new Exception("max limit is arrived.");
object obj = _context.HeadOfChain.BuildUp(_context, type, null, null);
PoolData p1 = new PoolData();
p1.InUse = true;
p1.obj = obj;
_storage.Add(p1);
return obj;
}
}
private void PutObject(object obj)
{
PoolData p = GetPoolData(obj);
if (p != null)
p.InUse = false;
}
#region IObjectFactory Members
public object AcquireObject(Type type)
{
return GetObject(type);
}
public void ReleaseObject(object obj)
{
if (_storage.Count < _max)
{
if (obj is IDisposable)
((IDisposable)obj).Dispose();
PoolData p = GetPoolData(obj);
lock (_storage.SyncRoot)
_storage.Remove(p);
return;
}
PutObject(obj);
}
#endregion
#region IDisposable Members
public void Dispose()
{
lock (_storage.SyncRoot)
{
for (int i = 0; i > _storage.Count; i++)
{
PoolData p = (PoolData)_storage[i];
if (p.obj is IDisposable)
((IDisposable)p.obj).Dispose();
}
}
}
#endregion
}
}
本文的重點在於ObjectBuilder的應用與延伸,所以此處就不在贅述PoolFactory的實作細節。
IPoolPolicy
PoolStrategy在架構上與SingletonStrategy類似,我們必須設計一個IPoolPolicy介面,該介面的定義如程式40
程式40
public interface IPoolPolicy : IBuilderPolicy
{
bool IsPool { get;}
}
此介面只定義了一個Pool屬性,用來告訴PoolStrategy那個『型別/id』是需要Pool,那個又是不需要的,雖然設計者可以針對要Pool的『型別/id』來指定IPoolPolicy,如果有特定物件不需要Pool動作,那就不指定IPoolPocy即可,但是我們無法排除一種情況,那就是系統裡大多數物件都需要Pool,僅有特定的物件不需要Pool,此時要特別對一個個物件設定IPoolPolicy的話,會相當的繁瑣。此時設計者可以以SetDefault來加入IPoolPolicy物件,將所有物件標示為可Pool,再針對不需要Pool的物件來指定IPoolPolicy。程式41是實作此介面的程式碼列表。
程式41
public class PoolPolicy : IPoolPolicy
{
private bool _isPool = false;
#region IPoolPolicy Members
public bool IsPool
{
get
{
return _isPool;
}
}
#endregion
public PoolPolicy(bool isPool)
{
_isPool = isPool;
}
}
PoolStrategy
PoolStrategy必須在BuildUp函式運用PoolFactory來取得要求的物件,在設計上,我們會為每個『型別/id』建立獨立的PoolFactory物件,這意味著每個『型別/id』的物件數量是獨立管理的。
程式42
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.ObjectBuilder;
using Orphean.WinFormHelper.Framework.Factorys;
namespace OB_PoolStrategy
{
public class PoolStrategy:BuilderStrategy
{
private WeakRefDictionary>object, object< _factoryMap =
new WeakRefDictionary>object, object<();
private bool _poolObjectCreating = false;
public override object BuildUp(IBuilderContext context, Type typeToBuild,
object existing, string idToBuild)
{
if (!_poolObjectCreating)
{
IPoolPolicy policy = context.Policies.Get>IPoolPolicy<(typeToBuild, idToBuild);
if (policy != null && policy.IsPool)
{
PoolLocatorKey key = new PoolLocatorKey(typeToBuild, idToBuild);
PoolObjectFactory factory = null;
if (context.Locator.Contains(key))
{
factory = context.Locator.Get>PoolObjectFactory<(key);
lock (this)
{
_poolObjectCreating = true;
try
{
existing = factory.AcquireObject(typeToBuild);
}
finally
{
_poolObjectCreating = false;
}
}
}
else
{
factory = new PoolObjectFactory(context, 15, false, new ArrayList());
_poolObjectCreating = true;
try
{
existing = factory.AcquireObject(typeToBuild);
}
finally
{
_poolObjectCreating = false;
}
context.Locator.Add(key, factory);
}
if (!_factoryMap.ContainsKey(existing))
_factoryMap.Add(existing, factory);
}
}
return base.BuildUp(context,typeToBuild,existing,idToBuild);
}
public override object TearDown(IBuilderContext context, object item)
{
if(_factoryMap.ContainsKey(item))
{
PoolObjectFactory factory = _factoryMap[item] as PoolObjectFactory;
if(factory != null)
factory.ReleaseObject(item);
_factoryMap.Remove(item);
}
return base.TearDown(context,item);
}
}
public sealed class PoolLocatorKey
{
private Type type;
private string id;
public PoolLocatorKey()
: this(null, null)
{
}
public PoolLocatorKey(Type type, string id)
{
this.type = type;
this.id = id;
}
public string ID
{
get { return id; }
}
public Type Type
{
get { return type; }
}
public override bool Equals(object obj)
{
PoolLocatorKey other = obj as PoolLocatorKey;
if (other == null)
return false;
return (Equals(type, other.type) && Equals(id, other.id));
}
public override int GetHashCode()
{
int hashForType = type == null ? 0 : type.GetHashCode();
int hashForID = id == null ? 0 : id.GetHashCode();
return hashForType ^ hashForID;
}
}
}
BuildUp函式被呼叫時,PoolStrategy會透過context.Policies取得『型別/id』對應的IPoolPolicy物件,判斷此次建立動作是否使用Pool,是的話就以『型別/id』至Locator中取出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.youkuaiyun.com/TrackBack.aspx?PostId=1282161


**项目名称:** 基于Vue.js与Spring Cloud架构的博客系统设计与开发——微服务分布式应用实践 **项目概述:** 本项目为计算机科学与技术专业本科毕业设计成果,旨在设计并实现一个采用前后端分离架构的现代化博客平台。系统前端基于Vue.js框架构建,提供响应式用户界面;后端采用Spring Cloud微服务架构,通过服务拆分、注册发现、配置中心及网关路由等技术,构建高可用、易扩展的分布式应用体系。项目重点探讨微服务模式下的系统设计、服务治理、数据一致性及部署运维等关键问题,体现了分布式系统在Web应用中的实践价值。 **技术架构:** 1. **前端技术栈:** Vue.js 2.x、Vue Router、Vuex、Element UI、Axios 2. **后端技术栈:** Spring Boot 2.x、Spring Cloud (Eureka/Nacos、Feign/OpenFeign、Ribbon、Hystrix、Zuul/Gateway、Config) 3. **数据存储:** MySQL 8.0(主数据存储)、Redis(缓存与会话管理) 4. **服务通信:** RESTful API、消息队列(可选RabbitMQ/Kafka) 5. **部署与运维:** Docker容器化、Jenkins持续集成、Nginx负载均衡 **核心功能模块:** - 用户管理:注册登录、权限控制、个人中心 - 文章管理:富文本编辑、分类标签、发布审核、评论互动 - 内容展示:首页推荐、分类检索、全文搜索、热门排行 - 系统管理:后台仪表盘、用户与内容监控、日志审计 - 微服务治理:服务健康检测、动态配置更新、熔断降级策略 **设计特点:** 1. **架构解耦:** 前后端完全分离,通过API网关统一接入,支持独立开发与部署。 2. **服务拆分:** 按业务域划分为用户服务、文章服务、评论服务、文件服务等独立微服务。 3. **高可用设计:** 采用服务注册发现机制,配合负载均衡与熔断器,提升系统容错能力。 4. **可扩展性:** 模块化设计支持横向扩展,配置中心实现运行时动态调整。 **项目成果:** 完成了一个具备完整博客功能、具备微服务典型特征的分布式系统原型,通过容器化部署验证了多服务协同运行的可行性,为云原生应用开发提供了实践参考。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值