SCSF 系列:Smart Client Software Factory 与 ObjectBuilder
【FLYabroad】ObjectBuilder 简介,SCSF 对 ObjectBuilder 的使用和扩展,SCSF 与控制反转(IOC)。
上一篇:Smart Client Software Factory 启动过程详解 介绍了 SCSF 的启动过程,启动的核心工作就是通过 ObjectBuilder 组件准备整个职能客户端运行环境,因此第一步就是建立一个 Microsoft.Practices.ObjectBuilder.Builder ,SCSF 的整个依赖注入(DI)就基于 ObjectBuilder 。
Object Builder 原先是微软P&P团队为 CAB(Composite UI Application Block,是 SCSF 的核心部分,本系列的大部分文章都是介绍 CAB 的) 建立的,后来几乎在所有P&P项目中使用,包括 Enterprise Libaray ,Web Client Software Factory,WebService Software Factory 等。本文说的 Object Builder 是 ObjectBuilder 1【FLYabroad注】,P&P 开发的 IOC 容器 Unity 是基于 ObjectBuilder 2 的(对 ObjectBuilder 1 进行了改进和加强) 。
通过名字我们大概可以看出 ObjectBuilder 是用来创建对象的,就像一个对象工厂,而这个工厂几乎可以制造任何对象,并且除了创建对象外还负责对象的生命周期管理(例如合适销毁,如何销毁等)。
为什么需要 ObjectBuilder ?原因说来也简单,一个对象要使用首先需要被创建(并初始化),传统的通过 new 方式来构造对象的方式是程序强耦合的罪魁:
- 首先 new 是具体化的,我们必须在程序中硬编码要 new 的对象,而面向对象的最佳实践是面向抽象;
- 其次,要 new 一个对象,new 的一方必须要引入被 new 一方的程序集依赖和命名空间,这就是强耦合;例如我们要构造 Builder 对象,必须先引入 ObjectBuilder.dll 程序集,同时 using Microsoft.Practices.ObjectBuilder.Builder ;
- 再次,我们所说的多态,如果只通过 new 来构造对象,有时多态的能力就会大打折扣;
要想构造松散耦合的系统,抽象对象构造过程是必须的。这一点在 IOC 模式中可以明显的体现出来,IOC 容器为我们提供了对象生命周期管理(从创建到销毁)的场所,它允许我们把对象构造的过程集中在容器中,可以通过配置或者元数据发现(Attribute)等方式动态的构造对象,并且容器提供强大的灵活性来适应对象的生命周期管理功能。这样对于我们构建松散耦合的系统,构造插件式系统简直太有用了。
如果希望程序能够动态的、抽象的构造对象就需要类似 ObjectBuilder 这样的组件或者服务(Sping 中的 ObjectFactory 也是完成这种工作的)。ObjectBuilder 是一个比较底层的对象创建组件,是 IOC 容器的基础。ObjectBuilder 就是为了简化和标准化这些工作的。
对于一般程序员来说我们直接使用 ObjectBuilder 的机会不多,同时网上也有很多不错的介绍 ObjectBuilder 的文章,这里我们不去过多的关注 OB 的工作原理和流程,而是系统通过 OB 在 SCSF 中的使用,更加透彻的理解上一篇我们讲的 SCSF 的工作原理。
一、 ObjectBuilder 的逻辑架构
ObjectBuilder 的核心概念由 4 个组件组成,BuilderContext(IBuilderContext) 是一个包含若干 Strategies (IBuilderStrategy)、Polices(IBuilderPolicy)、Locator(IReadableLocator,IReadWriteLocator)。
BuilderContext 维护了一个策略链(strategy chain),对象在经过策略链里的所有策略构造后才最终被创建;
Strategy 负责对象创建的一个方面,这些方面主要包括:Pre-Creation Strategy(对象被创建前),Creation Strategy(对象创建),Initialization Strategy (对象初始化),Post-Initialization Strategy (对象初始化完成后)。每个方面中又会有一或多个具体的策略类。例如 SingletonStrategy (属于 Pre-Creation Strategy)策略用于告诉 ObjectBuilder 应该创建单例对象。具体的 Strategy 通过 object BuildUp(IBuilderContext context, Type typeToBuild, object existing, string idToBuild) 构造对象,通过 object TearDown(IBuilderContext context, object item) 销毁对象。
Policy 的作用主要是告诉 Strategy 对不同的对象进行构建可能要进行不同操作 。Policy 与 Strategy 通常成对出现,例如 ISingletonPolicy 是配合 SingletonStrategy 在 CreationStrategy 中用的,只有那些被指明了 IsSingleton == true 属性的对象才使用 SingletonStrategy 。也就是 Policy 具体规定了对应 Strategy 的执行场合。
Locator 对象定位器,基本上是一个键值对的容器,分为 ReadOnlyLocator 和 ReadWriteLocator ,我们可以从 ReadOnlyLocator 获取对象,可以向 ReadWriteLocator 中添加、删除对象,当然也可以获取对象。
ILifetimeContainer 是一个生命周期容器,ObjectBuilder 通过 ILifetimeContainer 跟踪对象的生命周期,ILifetimeContainer 最主要的是实现了 IDisposable ,当 Container disposed 的时候,整个容器中的对象都将 disposed。
二、 SCSF 中主要的 Strategy 和 Policy
上一篇 中就介绍过 CreaterBuilder()方法注册了四个策略: EventBrokerStrategy,CommandStrategy,RootWorkItemInitializationStrategy ,ObjectBuiltNotificationStrategy 和三个缺省 Policy :SingletonPolicy,BuilderTraceSourcePolicy,ObjectBuiltNotificationPolicy 。
class CabApplication<TWorkItem> 中 Run() 最开始要做的就是为整个 SCSF 准备 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 扩展了 ObjectBuilder),三个 Policy 中 ObjectBuiltNotificationPolicy 是 CAB 提供的。下面我们重点介绍 SCSF 提供的这些 BuilderStrategies ,对应的源代码在 Microsoft.Practices.CompositeUI.BuilderStrategies 命名空间下。
1. EventBrokerStrategy
EventBrokerStrategy 应用于初始化阶段(BuilderStage.Initialization),是我们理解 SCSF EventBroker (EventPublicationAttribute 和 EventSubscriptionAttribute)的关键。
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 在构建过程中调用 EventInspector.Register(existing, workItem);(在销毁过程中执行相反操作 EventInspector.Unregister(item, workItem)),EventInspector.Register(existing, workItem) 中执行两个操作:
2 ProcessSubscribers(item, item.GetType(), workItem, true); //class EventInspector
ProcessPublishers 检查 item 中的 Event 上是否标有 EventPublicationAttribute 属性,如果有就注册到 EventTopic 的 Publication 中去:
ProcessSubscribers 检查相应的 item 中的成员上是否有 EventSubscriptionAttribute 属性,如果有就把它注册到对应得 Subscription 中去:
【FLYabroad】也就是说,SCSF 启动过程中,ObjectBuilder 会到被创建的对象上去找标有 EventPublicationAttribute 和 EventSubscriptionAttribute 的事件和方法,并通过 topic, 和 PublicationScope 把对应的事件发布和接受者动态的关联起来,放到 WorkItem 的 EventTopic 集合中。
2. CommandStrategy
CommandStrategy 与 EventBrokerStrategy 类似,主要负责查看构建的 item 的方法上是否有 CommandHandlerAttribute ,如果有就通过 Delegate 机制构建该对象并添加到当前 workItem 的 Commands 集合中。
3. RootWorkItemInitializationStrategy
RootWorkItemInitializationStrategy 在 BuilderStage.Initialization 阶段调用,并且将 RootWorkItemInitializationCallback 绑定到了 CabApplication 的 OnRootWorkItemInitialized 方法:
2 builder.Strategies.Add(new RootWorkItemInitializationStrategy(this.OnRootWorkItemInitialized), BuilderStage.Initialization);
3
整个 SCSF 应用有且只有一个 RootWorkItem ,ObjectBuilder 在创建 RootWorkItem 时通过调用 OnRootWorkItemInitialized 方法,CabApplication 中默认的 OnRootWorkItemInitialized 方法是一个空方法,子类 CabShellApplication 中重写了该方法,主要功能是将主窗体 TShell 加入到 RootWorkItem 中:
2 protected sealed override void OnRootWorkItemInitialized()
3 {
4 BeforeShellCreated();
5 shell = RootWorkItem.Items.AddNew<TShell>(); //将主窗口注册到 RootWorkItem 中
6 AfterShellCreated();
7 }
8
【FLYabroad】RootWorkItemInitializationStrategy 在构建 RootWorkItem 的过程中被使用,主要任务是将主窗口 TShell 注册到 RootWorkItem 中。
4. ObjectBuiltNotificationStrategy
ObjectBuiltNotificationStrategy 处理初始化完成后的事情(BuilderStage.PostInitialization),主要作用是在对象创建完成后通知相应的 workItem:
ObjectBuilder 在构造 RootWorkItem 对象过程中会调用 ObjectBuiltNotificationStrategy 的 BuildUp 方法,ObjectBuiltNotificationStrategy 又会调用 ObjectBuiltNotificationPolicy(在CreateBuilder()中注册)的 policy.AddedDelegates.TryGetValue(workItem, out notification) 获取 notification 代理(ObjectBuiltNotificationPolicy.ItemNotification 类型)并执行:
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):
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
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 内部使用,ManagedObjectCollection 将 WorkItem_ItemAdded() 方法绑定到了 WorkItem 的 ObjectAdded 事件,而 WorkItem_ItemAdded 方法又会触发 ManagedObjectCollection 的 Added 事件(public event EventHandler<DataEventArgs<TItem>> Added), SCSF 中 Added 事件默认没有注册,我们可以根据需要通过 ManagedObjectCollection 的 Added 事件处理对象构建完成后的事情。这又是 SCSF 的一个扩展点。
【FLYabroad】ObjectBuiltNotificationStrategy 结合 ObjectBuiltNotificationPolicy 允许我们通过在 ManagedObjectCollection 上注册 Added 事件来在对象构建完成后进行扩展处理。
三、SCSF 与控制反转(IOC\DI)
ObjectBuilder is a framework for creating dependency injection systems,SCSF 中使用 ObjectBuilder 来处理依赖注入。依赖注入的一般原理是,用一个容器来管理对象的生命周期,包需要的括创建、初始化、销毁,应用程序可以通过名字或者类型从容器中请求到需要的对象。
依赖注入常见的有三种:构造器注入、属性注入、方法注入。ObjectBuilder 通过 ConstructorReflectionStrategy, PropertyReflectionStrategy, MethodReflectionStrategy 三个策略来对应这三种注入方式。
ObjectBuilder 通过 Attribute 来判断具体进行哪种注入的,这些属性包括: [InjectionConstructor] ,[Dependency] ,[CreateNew] ,[MethodInjection] 。同时 SCSF 添加了自己的 Attribute :[ServiceDependency] , [ComponentDependency("id")],[TraceSource]。
SCSF 内部只使用了 [InjectionConstructor],[CreateNew],[ServiceDependency],[ComponentDependency("id")]和[TraceSource]。例如,GlobalBank.BasicAccounts.Module.PurchaseCDViewPresenter 的构造函数:
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 使用该构造方法创建该对象(因为一个类可能有多个构造函数)。下一步 ObjectBuilder 要为该构造函数准备参数(如果构造函数有参数的话),ObjectBuilder 同样还是根据参数前的 Attribute 来判断如何构建这些参数:
如果是 [CreateNew],则 ObjectBuilder 用相似的规则创建一个新的对象并传递给构造器;
如果是 [Dependency] ,OB 会在自己的对象容器中查找符合条件的已创建的对象;
如果是 [ServiceDependency],表示该参数依赖于一个已注册到 workItem 中的 service ,需要到 workItem 中去查找;
如果是 [ComponentDependency("id")],表示要到父 workItem 中找已经注册为“id”的对象;例如上例中 [ComponentDependency("QueueEntry")] QueueEntry queueEntry 的 queueEntry 就对应在 GlobalBank.BranchSystems.Module.CustomerWorkItemController 类的 Run() 方法中注册的 QueueEntry 实例:WorkItem.Items.Add(queueEntry, "QueueEntry");
[TraceSource] 表示依赖于跟踪源,SCSF 内部的 ClassNameTraceSourceAttribute 就是将类的全名作为一个跟踪源,我们平时使用的会比较少。
理解 SCSF 依赖注入的另一个要点是 SCSF 在 Microsoft.Practices.CompositeUI.Collections 提供的两个 Collections:
public class ServiceCollection : ICollection, IEnumerable>
它们在 WorkItem 类的 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
获得已经注册的对象,通过 Remove 从集合中删除对象。将在 WorkItem 中对这些 Collections 进行进一步介绍。
posted on 2008-06-17 12:06 FLYabroad 阅读(6886) 评论(9) 编辑 收藏 举报