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 > ( 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 进行进一步介绍。

Stkcd [股票代码] ShortName [股票简称] Accper [统计截止日期] Typrep [报表类型编码] Indcd [行业代码] Indnme [行业名称] Source [公告来源] F060101B [净利润现金净含量] F060101C [净利润现金净含量TTM] F060201B [营业收入现金含量] F060201C [营业收入现金含量TTM] F060301B [营业收入现金净含量] F060301C [营业收入现金净含量TTM] F060401B [营业利润现金净含量] F060401C [营业利润现金净含量TTM] F060901B [筹资活动债权人现金净流量] F060901C [筹资活动债权人现金净流量TTM] F061001B [筹资活动股东现金净流量] F061001C [筹资活动股东现金净流量TTM] F061201B [折旧摊销] F061201C [折旧摊销TTM] F061301B [公司现金流1] F061302B [公司现金流2] F061301C [公司现金流TTM1] F061302C [公司现金流TTM2] F061401B [股权现金流1] F061402B [股权现金流2] F061401C [股权现金流TTM1] F061402C [股权现金流TTM2] F061501B [公司自由现金流(原有)] F061601B [股权自由现金流(原有)] F061701B [全部现金回收率] F061801B [营运指数] F061901B [资本支出与折旧摊销比] F062001B [现金适合比率] F062101B [现金再投资比率] F062201B [现金满足投资比率] F062301B [股权自由现金流] F062401B [企业自由现金流] Indcd1 [行业代码1] Indnme1 [行业名称1] 季度数据,所有沪深北上市公司的 分别包含excel、dta数据文件格式及其说明,便于不同软件工具对数据的分析应用 数据来源:基于上市公司年报及公告数据整理,或相关证券交易所、各部委、省、市数据 数据范围:基于沪深北证上市公司 A股(主板、中小企业板、创业板、科创板等)数据整理计算
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值