您的位置:首页 > 移动开发 > Objective-C

SCSF 系列:Smart Client Software Factory 与 ObjectBuilder

2008-06-17 12:06 471 查看
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 :

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>(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 的构造函数

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, 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 进行进一步介绍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐