FLYabroad 技术移民 - FLYabroadVisa.com

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 的逻辑架构

BuilderContext

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 :

 1 private Builder CreateBuilder()
 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)的关键。

 1 //EventBrokerStrategy 类的 BuildUp 方法
 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) 中执行两个操作:

1 ProcessPublishers(item, item.GetType(), workItem, true); //class EventInspector
2 ProcessSubscribers(item, item.GetType(), workItem, true); //class EventInspector

ProcessPublishers 检查 item 中的 Event 上是否标有 EventPublicationAttribute 属性,如果有就注册到 EventTopic 的 Publication 中去:

1 topic.AddPublication(item, info.Name, workItem, attr.Scope); //class EventInspector 

ProcessSubscribers 检查相应的 item 中的成员上是否有 EventSubscriptionAttribute 属性,如果有就把它注册到对应得 Subscription 中去:

1 topic.AddSubscription(item, info.Name, paramTypes, workItem, attr.Thread); //class EventInspector 

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 方法:

1 //CabApplicatioin 类中的 CreatBuilder 方法中
2 builder.Strategies.Add(new RootWorkItemInitializationStrategy(this.OnRootWorkItemInitialized), BuilderStage.Initialization);
3 

整个 SCSF 应用有且只有一个 RootWorkItem ,ObjectBuilder 在创建 RootWorkItem 时通过调用 OnRootWorkItemInitialized 方法,CabApplication 中默认的 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 

FLYabroadRootWorkItemInitializationStrategy 在构建 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  类型)并执行:

 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>(nullnull); 
 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>(nullnull); 
 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 的构造函数
 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 的构造函数:

 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 使用该构造方法创建该对象(因为一个类可能有多个构造函数)。下一步 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 ManagedObjectCollection : ICollection, IEnumerable> 

public class ServiceCollection : ICollection, IEnumerable> 

它们在 WorkItem 类的 InitializeCollectionFacades() 方法中创建并初始化:

 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, nullnull, parent == null ? null : parent.workItemCollection);
19             }
20 
21             if (workspaceCollection == null)
22             {
23                 workspaceCollection = new ManagedObjectCollection<IWorkspace>(lifetime, locator, builder,
24                     SearchMode.Up, nullnull, parent == null ? null : parent.workspaceCollection);
25             }
26 
27             if (itemsCollection == null)
28             {
29                 itemsCollection = new ManagedObjectCollection<object>(lifetime, locator, builder,
30                     SearchMode.Local, nullnull, parent == null ? null : parent.itemsCollection);
31             }
32 
33             if (smartPartCollection == null)
34             {
35                 smartPartCollection = new ManagedObjectCollection<object>(lifetime, locator, builder,
36                     SearchMode.Local, nulldelegate(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, nullnull);
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编辑  收藏  举报

导航

FLYabroad 签证工作室 - FLYabroadVisa.com