CAL学习之二:容器(翻译)

容器:

基于CAL实现的程序,都是有一些松耦合的module构成,这些模块需要和Shell交互来实现数据的表现和相应用户的操作。因为他们都是松耦合的,所以他们需要一种相互交互和通讯的方式来实现所需要的业务功能。

为了配合这些不同的模块,基于CAL的应用程序使用了依赖注入的容器。依赖注入的容器可以减少对象之间的依赖,容器可以帮助我们实例化和维护一个类的实例的生命周期。这些都是依据对容器的配置。在一个对象的创建过程中,容器把这个对象需要的实例都注入进去。如果它需要的实例还没有创建,容器会先去创建再注入。有些时候,容器要把自己注入到对象中。比如一个module需要注入一个容器,这样module就可以把自己的view和service注册到容器中。

使用容器有以下好处:

  1. 容器省掉你下边的麻烦:你需要使用一个组件还要弄清楚它的依赖关系,以及创建和销毁它。
  2. 使用了容器,当你的类需要的组件的实现改变时,你的类不需要改变。
  3. 使用了容器,方便了你测试,因为你可以用一个假的依赖。
  4. 容器使系统易于维护,因为一个组件可以很容易的被加入进来。

对于CAL来说:容器有以下好处:

  1. 容器把一个module的依赖在他载入时注入。
  2. 容器被用来注册presenter和view。
  3. 容器创建Presenter和model,并把它们注入到view中。
  4. 容器可以注入服务,比如 regionmanager和event aggregator
  5. 容器可以用来注册模块相关的service。

下边的代码演示了injection是如何工作的,当PositionModule被container创建,它就会被注入regionManager和container。在RegisterViewsAndServices方法中,service,view和presenter都被注册。

publicPositionModule(IUnityContainer container, IRegionManager regionManager)

{

    _container = container;

    _regionManagerService = regionManager;

}

 

public voidInitialize()

{

    RegisterViewsAndServices();

    ...

}

 

protected voidRegisterViewsAndServices()

{

   _container.RegisterType<IAccountPositionService,AccountPositionService>(new ContainerControlledLifetimeManager());

   _container.RegisterType<IPositionSummaryView,PositionSummaryView>();

   _container.RegisterType<IPositionSummaryPresentationModel,PositionSummaryPresentationModel>();

    ...

}

使用Container

Container主要有两种用途:注册和解析。

注册:

在你要对一个对象的依赖进行注入之前,依赖的类型必须被注册到container中,注册一个类型需要传给container一个接口和实现这个接口的具体类型。你可以通过代码也可以通过配置来进行注册。

通过代码你有两种方法来注册类型和对象到container中。

  1. 你可以注册一个类型或者一个映射到container,在合适的时候,container会自己创建一个你指定类型的实例。
  2. 你可以注册一个已经生成的对象到container,container将会返回一个这个对象的引用。

this.container.RegisterType<IEmployeesController,EmployeesController>();

上边的代码用第一种方法来实现注册。

注册到container中的类型有一个生存期,可以是singleton或者是instance(每次都创建一个新的)。生存期可以在注册时或者在配置文件中指定。默认的生存期由container的实现决定,比如Unity container默认的是instance模式。

解析(Resolving)

当一个类型被注册后,就可以被解析或者作为一个依赖注入。

一般情况下,当一个类型解析出来,会有下边三种情况:

1.这个类型没有注册,container会抛出异常。

2.如果这个类型被注册为一个singleton模式,container将会返回一个singleton实例,如果这是第一次请求,就创建一个,并且保留这个实例,以备后用。

  1. 如果这个类型不是singleton的,就生成一个实例返回,并不保留对实例的引用。

下边的代码就是从container中解析一个类型:

EmployeesPresenterpresenter = this.container.Resolve<EmployeesPresenter>();

但是EmployeesPresenter的构造函数有下边的依赖,这些依赖在类型解析时会被注入实例。

publicEmployeesPresenter(IEmployeesView view, IEmployeesListPresenter listPresenter, IEmployeesControlleremployeeController)

    {

        this.View = view;

        this.listPresenter = listPresenter;

        this.listPresenter.EmployeeSelected +=newEventHandler<DataEventArgs<BusinessEntities.Employee>>(this.OnEmployeeSelected);

        this.employeeController = employeeController;

        View.SetHeader(listPresenter.View);

    }

什么时候使用容器?

考虑使用容器来进行注册和解析是否合适。

你的应用是否能接受注册和解析所消耗的性能。因为容器时利用了反射。

如果依赖过多或者层次多深,性能消耗会明显增加。

如果一个组件没有任何依赖,或者其他类型也不依赖他,那么把它放到容器中就没有什么意义。

考虑对象的生存期,是否用singleton?

如果一个组件是一个全局的服务,比如log,我们将它注册成单例模式。

如果一个组件对多个客户提供状态共享,你可以将它注册成单例模式。

如果有的依赖每次有需要一个新的实例,你可以将它注册成一个非单例模式。

考虑你是通过代码还是配置文件来配置container。

如果你想集中管理不同的服务,那就通过配置文件来配置。

如果你想根据不同的情况注册不同的service,那就要通过代码来配置。

如果你有模块级别的服务,那么就用代码来注册这些服务,因为这样就可以只在模块载入时再注册服务。

posted @ 2010-01-28 13:31  mark_xue  阅读(347)  评论(0编辑  收藏  举报