SCSF 系列:Smart Client Software Factory 与 ObjectBuilder
【FLYabroad】ObjectBuilder 簡介,SCSF 對 ObjectBuilder 的使用和擴(kuò)展,SCSF 與控制反轉(zhuǎn)(IOC)。
上一篇:Smart Client Software Factory 啟動過程詳解 介紹了 SCSF 的啟動過程,啟動的核心工作就是通過 ObjectBuilder 組件準(zhǔn)備整個職能客戶端運(yùn)行環(huán)境,因此第一步就是建立一個 Microsoft.Practices.ObjectBuilder.Builder ,SCSF 的整個依賴注入(DI)就基于 ObjectBuilder 。
Object Builder 原先是微軟P&P團(tuán)隊(duì)為 CAB(Composite UI Application Block,是 SCSF 的核心部分,本系列的大部分文章都是介紹 CAB 的) 建立的,后來幾乎在所有P&P項(xiàng)目中使用,包括 Enterprise Libaray ,Web Client Software Factory,WebService Software Factory 等。本文說的 Object Builder 是 ObjectBuilder 1【FLYabroad注】,P&P 開發(fā)的 IOC 容器 Unity 是基于 ObjectBuilder 2 的(對 ObjectBuilder 1 進(jìn)行了改進(jìn)和加強(qiáng)) 。
通過名字我們大概可以看出 ObjectBuilder 是用來創(chuàng)建對象的,就像一個對象工廠,而這個工廠幾乎可以制造任何對象,并且除了創(chuàng)建對象外還負(fù)責(zé)對象的生命周期管理(例如合適銷毀,如何銷毀等)。
為什么需要 ObjectBuilder ?原因說來也簡單,一個對象要使用首先需要被創(chuàng)建(并初始化),傳統(tǒng)的通過 new 方式來構(gòu)造對象的方式是程序強(qiáng)耦合的罪魁:
- 首先 new 是具體化的,我們必須在程序中硬編碼要 new 的對象,而面向?qū)ο蟮淖罴褜?shí)踐是面向抽象;
- 其次,要 new 一個對象,new 的一方必須要引入被 new 一方的程序集依賴和命名空間,這就是強(qiáng)耦合;例如我們要構(gòu)造 Builder 對象,必須先引入 ObjectBuilder.dll 程序集,同時 using Microsoft.Practices.ObjectBuilder.Builder ;
- 再次,我們所說的多態(tài),如果只通過 new 來構(gòu)造對象,有時多態(tài)的能力就會大打折扣;
要想構(gòu)造松散耦合的系統(tǒng),抽象對象構(gòu)造過程是必須的。這一點(diǎn)在 IOC 模式中可以明顯的體現(xiàn)出來,IOC 容器為我們提供了對象生命周期管理(從創(chuàng)建到銷毀)的場所,它允許我們把對象構(gòu)造的過程集中在容器中,可以通過配置或者元數(shù)據(jù)發(fā)現(xiàn)(Attribute)等方式動態(tài)的構(gòu)造對象,并且容器提供強(qiáng)大的靈活性來適應(yīng)對象的生命周期管理功能。這樣對于我們構(gòu)建松散耦合的系統(tǒng),構(gòu)造插件式系統(tǒng)簡直太有用了。
如果希望程序能夠動態(tài)的、抽象的構(gòu)造對象就需要類似 ObjectBuilder 這樣的組件或者服務(wù)(Sping 中的 ObjectFactory 也是完成這種工作的)。ObjectBuilder 是一個比較底層的對象創(chuàng)建組件,是 IOC 容器的基礎(chǔ)。ObjectBuilder 就是為了簡化和標(biāo)準(zhǔn)化這些工作的。
對于一般程序員來說我們直接使用 ObjectBuilder 的機(jī)會不多,同時網(wǎng)上也有很多不錯的介紹 ObjectBuilder 的文章,這里我們不去過多的關(guān)注 OB 的工作原理和流程,而是系統(tǒng)通過 OB 在 SCSF 中的使用,更加透徹的理解上一篇我們講的 SCSF 的工作原理。
一、 ObjectBuilder 的邏輯架構(gòu)
ObjectBuilder 的核心概念由 4 個組件組成,BuilderContext(IBuilderContext) 是一個包含若干 Strategies (IBuilderStrategy)、Polices(IBuilderPolicy)、Locator(IReadableLocator,IReadWriteLocator)。
BuilderContext 維護(hù)了一個策略鏈(strategy chain),對象在經(jīng)過策略鏈里的所有策略構(gòu)造后才最終被創(chuàng)建;
Strategy 負(fù)責(zé)對象創(chuàng)建的一個方面,這些方面主要包括:Pre-Creation Strategy(對象被創(chuàng)建前),Creation Strategy(對象創(chuàng)建),Initialization Strategy (對象初始化),Post-Initialization Strategy (對象初始化完成后)。每個方面中又會有一或多個具體的策略類。例如 SingletonStrategy (屬于 Pre-Creation Strategy)策略用于告訴 ObjectBuilder 應(yīng)該創(chuàng)建單例對象。具體的 Strategy 通過 object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild) 構(gòu)造對象,通過 object TearDown(IBuilderContext context, object item) 銷毀對象。
Policy 的作用主要是告訴 Strategy 對不同的對象進(jìn)行構(gòu)建可能要進(jìn)行不同操作 。Policy 與 Strategy 通常成對出現(xiàn),例如 ISingletonPolicy 是配合 SingletonStrategy 在 CreationStrategy 中用的,只有那些被指明了 IsSingleton == true 屬性的對象才使用 SingletonStrategy? 。也就是 Policy 具體規(guī)定了對應(yīng) Strategy 的執(zhí)行場合。
Locator 對象定位器,基本上是一個鍵值對的容器,分為 ReadOnlyLocator 和 ReadWriteLocator ,我們可以從 ReadOnlyLocator 獲取對象,可以向 ReadWriteLocator? 中添加、刪除對象,當(dāng)然也可以獲取對象。
ILifetimeContainer 是一個生命周期容器,ObjectBuilder 通過 ILifetimeContainer 跟蹤對象的生命周期,ILifetimeContainer 最主要的是實(shí)現(xiàn)了 IDisposable ,當(dāng) Container disposed 的時候,整個容器中的對象都將 disposed。
二、 SCSF 中主要的 Strategy 和 Policy
上一篇 中就介紹過 CreaterBuilder()方法注冊了四個策略: EventBrokerStrategy,CommandStrategy,RootWorkItemInitializationStrategy ,ObjectBuiltNotificationStrategy 和三個缺省 Policy :SingletonPolicy,BuilderTraceSourcePolicy,ObjectBuiltNotificationPolicy 。
class CabApplication<TWorkItem> 中 Run() 最開始要做的就是為整個 SCSF 準(zhǔn)備 Builder :
?2?{
?3?????Builder?builder?=?new?Builder();?
?4?????builder.Strategies.AddNew<EventBrokerStrategy>(BuilderStage.Initialization);
?5?????builder.Strategies.AddNew<CommandStrategy>(BuilderStage.Initialization);
?6?????builder.Strategies.Add(new?RootWorkItemInitializationStrategy(this.OnRootWorkItemInitialized),?BuilderStage.Initialization);
?7?????builder.Strategies.AddNew<ObjectBuiltNotificationStrategy>(BuilderStage.PostInitialization);?
?8?
?9?builder.Policies.SetDefault<ISingletonPolicy>(new?SingletonPolicy(true));
10?????builder.Policies.SetDefault<IBuilderTracePolicy>(new?BuilderTraceSourcePolicy(new?TraceSource("Microsoft.Practices.ObjectBuilder")));
11?????builder.Policies.SetDefault<ObjectBuiltNotificationPolicy>(new?ObjectBuiltNotificationPolicy());?
12?
13?????return?builder;
14?}?
這四個策略(Strategy)都是由 SCSF(CAB) 提供的(也就是 CAB 擴(kuò)展了 ObjectBuilder),三個 Policy 中 ObjectBuiltNotificationPolicy 是 CAB 提供的。下面我們重點(diǎn)介紹 SCSF 提供的這些 BuilderStrategies ,對應(yīng)的源代碼在 Microsoft.Practices.CompositeUI.BuilderStrategies 命名空間下。
1. EventBrokerStrategy
EventBrokerStrategy 應(yīng)用于初始化階段(BuilderStage.Initialization),是我們理解 SCSF EventBroker (EventPublicationAttribute 和 EventSubscriptionAttribute)的關(guān)鍵。
?2?public?override?object?BuildUp(IBuilderContext?context,?Type?t,?object?existing,?string?id)
?3?{
?4?????WorkItem?workItem?=?GetWorkItem(context,?existing);?
?5?
?6?????if?(workItem?!=?null)
?7?????????EventInspector.Register(existing,?workItem);?
?8?
?9?????return?base.BuildUp(context,?t,?existing,?id);
10?}?
EventBrokerStrategy 告訴 ObjectBuilder 在構(gòu)建過程中調(diào)用 EventInspector.Register(existing, workItem);(在銷毀過程中執(zhí)行相反操作 EventInspector.Unregister(item, workItem)),EventInspector.Register(existing, workItem) 中執(zhí)行兩個操作:
1?ProcessPublishers(item,?item.GetType(),?workItem,?true);?//class?EventInspector2?ProcessSubscribers(item,?item.GetType(),?workItem,?true);?//class?EventInspector
ProcessPublishers 檢查 item 中的 Event 上是否標(biāo)有 EventPublicationAttribute 屬性,如果有就注冊到 EventTopic 的 Publication 中去:
1?topic.AddPublication(item,?info.Name,?workItem,?attr.Scope);?//class?EventInspector?ProcessSubscribers 檢查相應(yīng)的 item 中的成員上是否有 EventSubscriptionAttribute 屬性,如果有就把它注冊到對應(yīng)得 Subscription 中去:
【FLYabroad】也就是說,SCSF 啟動過程中,ObjectBuilder 會到被創(chuàng)建的對象上去找標(biāo)有 EventPublicationAttribute 和 EventSubscriptionAttribute 的事件和方法,并通過 topic, 和 PublicationScope 把對應(yīng)的事件發(fā)布和接受者動態(tài)的關(guān)聯(lián)起來,放到 WorkItem 的 EventTopic 集合中。
2. CommandStrategy
CommandStrategy 與 EventBrokerStrategy 類似,主要負(fù)責(zé)查看構(gòu)建的 item 的方法上是否有 CommandHandlerAttribute ,如果有就通過 Delegate 機(jī)制構(gòu)建該對象并添加到當(dāng)前 workItem 的 Commands 集合中。
3. RootWorkItemInitializationStrategy?
RootWorkItemInitializationStrategy 在 BuilderStage.Initialization 階段調(diào)用,并且將 RootWorkItemInitializationCallback 綁定到了 CabApplication 的 OnRootWorkItemInitialized 方法:
1?//CabApplicatioin?類中的?CreatBuilder?方法中2?builder.Strategies.Add(new?RootWorkItemInitializationStrategy(this.OnRootWorkItemInitialized),?BuilderStage.Initialization);
3?
整個 SCSF 應(yīng)用有且只有一個 RootWorkItem ,ObjectBuilder 在創(chuàng)建 RootWorkItem 時通過調(diào)用 OnRootWorkItemInitialized 方法,CabApplication 中默認(rèn)的 OnRootWorkItemInitialized 方法是一個空方法,子類 CabShellApplication 中重寫了該方法,主要功能是將主窗體 TShell 加入到 RootWorkItem 中:
1?//CabShellApplication?類重寫了父類?CabApplication?空的?OnRootWorkItemInitialized?方法2?protected?sealed?override?void?OnRootWorkItemInitialized()
3?{
4?????BeforeShellCreated();
5?????shell?=?RootWorkItem.Items.AddNew<TShell>();?//將主窗口注冊到?RootWorkItem?中
6?????AfterShellCreated();
7?}?
8?
【FLYabroad】RootWorkItemInitializationStrategy 在構(gòu)建 RootWorkItem 的過程中被使用,主要任務(wù)是將主窗口 TShell 注冊到 RootWorkItem 中。
4. ObjectBuiltNotificationStrategy
ObjectBuiltNotificationStrategy 處理初始化完成后的事情(BuilderStage.PostInitialization),主要作用是在對象創(chuàng)建完成后通知相應(yīng)的 workItem:
ObjectBuilder 在構(gòu)造 RootWorkItem 對象過程中會調(diào)用 ObjectBuiltNotificationStrategy 的 BuildUp 方法,ObjectBuiltNotificationStrategy 又會調(diào)用 ObjectBuiltNotificationPolicy(在CreateBuilder()中注冊)的 policy.AddedDelegates.TryGetValue(workItem, out notification) 獲取 notification 代理(ObjectBuiltNotificationPolicy.ItemNotification? 類型)并執(zhí)行:
?1?//ObjectBuiltNotificationStrategy?類中的?BuildUp?方法?2?public?override?object?BuildUp(IBuilderContext?context,?Type?typeToBuild,?object?existing,?string?idToBuild)
?3?{
?4?????WorkItem?workItem?=?context.Locator.Get<WorkItem>(new?DependencyResolutionLocatorKey(typeof(WorkItem),?null));
?5?????ObjectBuiltNotificationPolicy.ItemNotification?notification;?
?6?
?7?????if?(policy?==?null)
?8?????????policy?=?context.Policies.Get<ObjectBuiltNotificationPolicy>(null,?null);?
?9?
10?????if?(workItem?!=?null?&&?!Object.ReferenceEquals(workItem,?existing)?&&?policy.AddedDelegates.TryGetValue(workItem,?out?notification))
11?????????notification(existing);?
12?
13?????return?base.BuildUp(context,?typeToBuild,?existing,?idToBuild);
14?}?
15?
ObjectBuiltNotificationStrategy 的 AddedDelegates 是在 WorkItem 初始化時賦值的(在InitializeFields()中注冊 OnObjectAdded):
?1?//WorkItem?的?InitializeFields()?方法中??2?……………………?
?3?ObjectBuiltNotificationPolicy?policy?=?builder.Policies.Get<ObjectBuiltNotificationPolicy>(null,?null);?
?4?
?5?if?(policy?!=?null)
?6?{
?7?????policy.AddedDelegates[this]?=?new?ObjectBuiltNotificationPolicy.ItemNotification(OnObjectAdded);
?8?????policy.RemovedDelegates[this]?=?new?ObjectBuiltNotificationPolicy.ItemNotification(OnObjectRemoved);
?9?}?
10?……………………………
11?
WorkItem 中的 internal event EventHandler> ObjectAdded;是在 ManagedObjectCollection 中注冊:
?1?//ManagedObjectCollection?的構(gòu)造函數(shù)?2?public?ManagedObjectCollection(ILifetimeContainer?container,?IReadWriteLocator?locator,
?3?????????????IBuilder<BuilderStage>?builder,?SearchMode?searchMode,?IndexerCreationDelegate?indexerCreationDelegate,
?4?????????????Predicate<TItem>?filter,?ManagedObjectCollection<TItem>?parentCollection)
?5?????????{
?6?????????????this.container?=?container;
?7?????????????this.locator?=?locator;
?8?????????????this.builder?=?builder;
?9?????????????this.searchMode?=?searchMode;
10?????????????this.indexerCreationDelegate?=?indexerCreationDelegate;
11?????????????this.filter?=?filter;
12?????????????this.parentCollection?=?parentCollection;
13?????????????this.workItem?=?locator.Get<WorkItem>(new?DependencyResolutionLocatorKey(typeof(WorkItem),?null));
14?
15?????????????if?(this.workItem?!=?null)
16?????????????{
17?????????????????this.workItem.ObjectAdded?+=?new?EventHandler<DataEventArgs<object>>(WorkItem_ItemAdded);
18?????????????????this.workItem.ObjectRemoved?+=?new?EventHandler<DataEventArgs<object>>(WorkItem_ItemRemoved);
19?????????????}
20?????????}
WorkItem 的 ObjectAdded 是 internal 的,只能在 CompositeUI.dll 內(nèi)部使用,ManagedObjectCollection? 將 WorkItem_ItemAdded() 方法綁定到了 WorkItem 的 ObjectAdded 事件,而 WorkItem_ItemAdded 方法又會觸發(fā) ManagedObjectCollection 的 Added 事件(public event EventHandler<DataEventArgs<TItem>> Added), SCSF 中 Added 事件默認(rèn)沒有注冊,我們可以根據(jù)需要通過 ManagedObjectCollection 的 Added 事件處理對象構(gòu)建完成后的事情。這又是 SCSF 的一個擴(kuò)展點(diǎn)。
【FLYabroad】ObjectBuiltNotificationStrategy? 結(jié)合 ObjectBuiltNotificationPolicy 允許我們通過在 ManagedObjectCollection 上注冊 Added 事件來在對象構(gòu)建完成后進(jìn)行擴(kuò)展處理。
三、SCSF 與控制反轉(zhuǎn)(IOC\DI)
ObjectBuilder is a framework for creating dependency injection systems,SCSF 中使用 ObjectBuilder 來處理依賴注入。依賴注入的一般原理是,用一個容器來管理對象的生命周期,包需要的括創(chuàng)建、初始化、銷毀,應(yīng)用程序可以通過名字或者類型從容器中請求到需要的對象。
依賴注入常見的有三種:構(gòu)造器注入、屬性注入、方法注入。ObjectBuilder 通過 ConstructorReflectionStrategy, PropertyReflectionStrategy, MethodReflectionStrategy 三個策略來對應(yīng)這三種注入方式。
ObjectBuilder 通過 Attribute 來判斷具體進(jìn)行哪種注入的,這些屬性包括: [InjectionConstructor] ,[Dependency] ,[CreateNew] ,[MethodInjection] 。同時 SCSF 添加了自己的 Attribute :[ServiceDependency] , [ComponentDependency("id")],[TraceSource]。
SCSF 內(nèi)部只使用了 [InjectionConstructor],[CreateNew],[ServiceDependency],[ComponentDependency("id")]和[TraceSource]。例如,GlobalBank.BasicAccounts.Module.PurchaseCDViewPresenter 的構(gòu)造函數(shù):
?1?????????[InjectionConstructor]?2?????????public?PurchaseCDViewPresenter
?3?????????????(
?4?????????????[ComponentDependency("QueueEntry")]?QueueEntry?queueEntry,
?5?????????????[ServiceDependency]?IQuoteService?quoteService,
?6?????????????[ServiceDependency]?ICustomerAccountService?customerAccountsService,
?7?????????????[ServiceDependency]?IAccountService?accountService
?8?????????????)
?9?????????{
10?????????????_queueEntry?=?queueEntry;
11?????????????_quoteService?=?quoteService;
12?????????????_customerAccountsService?=?customerAccountsService;
13?????????????_accountService?=?accountService;
14?????????}
[InjectionConstructor] 用于告訴 ObjectBuilder 使用該構(gòu)造方法創(chuàng)建該對象(因?yàn)橐粋€類可能有多個構(gòu)造函數(shù))。下一步 ObjectBuilder 要為該構(gòu)造函數(shù)準(zhǔn)備參數(shù)(如果構(gòu)造函數(shù)有參數(shù)的話),ObjectBuilder 同樣還是根據(jù)參數(shù)前的 Attribute 來判斷如何構(gòu)建這些參數(shù):
如果是 [CreateNew],則 ObjectBuilder 用相似的規(guī)則創(chuàng)建一個新的對象并傳遞給構(gòu)造器;
如果是 [Dependency] ,OB 會在自己的對象容器中查找符合條件的已創(chuàng)建的對象;
如果是 [ServiceDependency],表示該參數(shù)依賴于一個已注冊到 workItem 中的 service ,需要到 workItem 中去查找;
如果是 [ComponentDependency("id")],表示要到父 workItem 中找已經(jīng)注冊為“id”的對象;例如上例中 [ComponentDependency("QueueEntry")] QueueEntry queueEntry 的 queueEntry 就對應(yīng)在 GlobalBank.BranchSystems.Module.CustomerWorkItemController 類的 Run() 方法中注冊的 QueueEntry 實(shí)例:WorkItem.Items.Add(queueEntry, "QueueEntry");
[TraceSource] 表示依賴于跟蹤源,SCSF 內(nèi)部的 ClassNameTraceSourceAttribute 就是將類的全名作為一個跟蹤源,我們平時使用的會比較少。
理解 SCSF 依賴注入的另一個要點(diǎn)是 SCSF 在 Microsoft.Practices.CompositeUI.Collections 提供的兩個 Collections:
public?class?ManagedObjectCollection?:?ICollection,?IEnumerable>?public?class?ServiceCollection?:?ICollection,?IEnumerable>?
它們在 WorkItem 類的 InitializeCollectionFacades() 方法中創(chuàng)建并初始化:
?1?????????private?void?InitializeCollectionFacades()?2?????????{
?3?????????????if?(serviceCollection?==?null)
?4?????????????{
?5?????????????????serviceCollection?=?new?ServiceCollection(lifetime,?locator,?builder,
?6?????????????????????parent?==?null???null?:?parent.serviceCollection);
?7?????????????}
?8?
?9?????????????if?(commandCollection?==?null)
10?????????????{
11?????????????????commandCollection?=?new?ManagedObjectCollection<Command>(lifetime,?locator,?builder,
12?????????????????????SearchMode.Up,?CreateCommand,?null,?parent?==?null???null?:?parent.commandCollection);
13?????????????}
14?
15?????????????if?(workItemCollection?==?null)
16?????????????{
17?????????????????workItemCollection?=?new?ManagedObjectCollection<WorkItem>(lifetime,?locator,?builder,
18?????????????????????SearchMode.Local,?null,?null,?parent?==?null???null?:?parent.workItemCollection);
19?????????????}
20?
21?????????????if?(workspaceCollection?==?null)
22?????????????{
23?????????????????workspaceCollection?=?new?ManagedObjectCollection<IWorkspace>(lifetime,?locator,?builder,
24?????????????????????SearchMode.Up,?null,?null,?parent?==?null???null?:?parent.workspaceCollection);
25?????????????}
26?
27?????????????if?(itemsCollection?==?null)
28?????????????{
29?????????????????itemsCollection?=?new?ManagedObjectCollection<object>(lifetime,?locator,?builder,
30?????????????????????SearchMode.Local,?null,?null,?parent?==?null???null?:?parent.itemsCollection);
31?????????????}
32?
33?????????????if?(smartPartCollection?==?null)
34?????????????{
35?????????????????smartPartCollection?=?new?ManagedObjectCollection<object>(lifetime,?locator,?builder,
36?????????????????????SearchMode.Local,?null,?delegate(object?obj)
37?????????????????????????{
38?????????????????????????????return?obj.GetType().GetCustomAttributes(typeof(SmartPartAttribute),?true).Length?>?0;
39?????????????????????????},
40?????????????????????parent?==?null???null?:?parent.smartPartCollection);
41?????????????}
42?
43?????????????if?(eventTopicCollection?==?null)
44?????????????{
45?????????????????if?(parent?==?null)
46?????????????????????eventTopicCollection?=?new?ManagedObjectCollection<EventTopic>(lifetime,?locator,?builder,
47?????????????????????????SearchMode.Local,?CreateEventTopic,?null,?null);
48?????????????????else
49?????????????????????eventTopicCollection?=?RootWorkItem.eventTopicCollection;
50?????????????}
51?
52?????????????if?(uiExtensionSiteCollection?==?null)
53?????????????{
54?????????????????if?(parent?==?null)
55?????????????????????uiExtensionSiteCollection?=?new?UIExtensionSiteCollection(this);
56?????????????????else
57?????????????????????uiExtensionSiteCollection?=?new?UIExtensionSiteCollection(parent.uiExtensionSiteCollection);
58?????????????}
59?????????}
ObjectBuilder (我們在使用 SCSF 時也是這樣)一般是使用這些 Collections 的 Add、AddNew 方法將對象添加到集合中,通過 Get、FindByType
獲得已經(jīng)注冊的對象,通過 Remove 從集合中刪除對象。將在 WorkItem 中對這些 Collections 進(jìn)行進(jìn)一步介紹。
轉(zhuǎn)載于:https://www.cnblogs.com/flyabroad/archive/2008/06/17/1223741.html
總結(jié)
以上是生活随笔為你收集整理的SCSF 系列:Smart Client Software Factory 与 ObjectBuilder的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: XML DTD用法【转载】
- 下一篇: 开源的C#组件——RSS.NET