基于Prism库的应用程序是由多个松耦合的类型和服务组成。组件需要和用户动作产生的内容和通知交互。因为组件式松耦合的,组件需要和其他组件交互。要把这些连接起来,Prism使用了依赖注入容器。

依赖注入容器减少对象间的耦合关系,容器通过配置管理对象的实例化,生命周期。对象创建期间,容器注入对象所需的依赖项。如果这些依赖性没被创建,容器就先创建和解析依赖项。一些情况下,容器本身作为一个依赖性被解析。例如,使用Unity作为容器,模块需要注入容器,这样模块才能注册其视图和服务到容器中。

使用容器的诸般好处:

  • 容器移除了对象对于其依赖项生命周期的管理。
  • 容器允许交换依赖项的实现而不影响组件。
  • 容器功能有助于可测性,因为依赖项是可以模拟的。
  • 容器提升易维护性。新组件可以轻松添加到系统中。

在基于上述Prism库的应用程序的上下文中,容器有一些特别的优势:

  • 当模块加载时,容器将模块依赖项注入给模块。
  • 容器可以用来注册和解析视图模型和视图。
  • 容器创建视图模型并将其注入给视图。
  • 给容器注入组件服务,例如区域管理器和事件聚合器。
  • 当程序需要某模块功能时,容器根据需要注册模块服务。

注意:一些示例使用的Untiy,还有一些示例使用的MEF。Prism本身不是必须需要某容器的。你可以用其他容器,例如Castle Windsor, StructureMap, 和Spring.NET。

关键决定:选择一个依赖注入容器

Prism提供了两个选择:Unity或是MEF。Prism是可扩展的,因此花费一点点工作量也可以使用其他容器。尽管工作方式不同,Untiy和MEF都提供了依赖注入的基本功能:

  • 都可以利用容器注册类型
  • 都可以利用容器注册实例
  • 都可以马上创建出注册的类型的实例。
  • 都可以注入注册类型的实例到构造器。
  • 都可以注入注册类型的实例到属性。
  • 都需要声明特性以标记类型和需要管理的依赖项。
  • 都在一个对象图中解析依赖项。

Untiy独有功能:

  • 可以解析没注册的类型。
  • 可以解析开泛型。
  • 可以利用截断类获取调用对象并添加额外的功能到调用对象上。

MEF独有功能:

  • 在路径中发现程序集。
  • 使用XAP文件下载并发现程序。
  • 重新组合属性和集合作为新对象被发现。
  • 自动导出继承类型。
  • 它可以同 .NET Framework一同部署。

容器拥有不同的功能和工作方式。但Prism库可以和它们一同工作并提供类似的功能。当考虑要用那个容器时,考虑它的能力适不适合你的方案。

使用容器的考虑

在使用容器之前你该考虑以下内容:

  • 考虑是否适合使用容器注册和解析组件:

    • 考虑容器注册和解析的性能是否符合你的方案。例如,如果你需要在一个呈现方法里创建10000个多边形,解析多边形肯定严重影响性能,因为容器会为每个多边形创建一个实体。
    • 如果依赖项太多,或是有深层次的依赖关系,创建对象的花销会明显提高
    • 如果组件没有任何依赖项,也没必要使用容器。
    • 如果组件有一个依赖项就集成在类型里并且不会有什么改变,也没必要将其弄进容器里。
  • 考虑组件是否应该被注册成单例:
  • 如果组件是一个全局服务,是一个单一资源的管理器,例如日志服务,你需要将其注册为单例。
  • 如果组件提供共享状态给多个用户,你需要将其注册为单例。
  • 如果对象每次都需要一个新实例作为依赖对象,那注册它为一个非单例。例如,每个视图都需要一个新实例的视图模型。
  • 考虑你配置容器使用代码还是配置文件:
  • 如果你想集中管理所有不同的服务,那么使用配置文件。
  • 如果你想根据条件进行注册特别的服务,那么使用代码配置。
  • 如果你有个模块级别的服务,考虑用代码配置,以便只有模块加载后才注册这个服务。
注意
例如MEF的容器不能陪在一个配置文件,必须使用代码进行配置。

核心方案

容器的使用主要有两个目的,即注册和解析。

注册

在你可以注册依赖项到一个对象之前,此对象类型的依赖项需要被先注册到容器里。注册一个类型通常涉及传递给容器一个接口和一个实现此接口的具体类型。主要有两种方法用于注册类型和对象:通过代码或通过配置文件。具体的方法时多样的。

通常,通过代码有两种方式注册类型和对象到容器中。

  • 你可以注册一个类型或一个映射到容器中,在适当的时机,容器将建立一个你指向的实例。
  • 你可以注册一个已存在的实例到你的容器中。将其作为一个单例。解析时容器将返回此实例的引用。

Unity容器注册类型

在初始化期间,类型可以注册其他类型,例如视图和服务。注册允许通过容器来提供它们的依赖项,并允许它们可以从其它类型的存取。为做到这点,类型将需要将容器注入到模块构造器。以下代码显示OrderModule 类型(来自命令快速入门)

// OrderModule.cs
public class OrderModule : IModule
{public void Initialize(){this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());...}...
}

这取决于您所使用的容器,注册页可以用配置文件,想看这个例子,请看利用配置文件注册模块(来自模块化应用程序开发)

注意
相比于在配置中注册,代码注册的好处是只有在模块加载了才注册模块需要的类型。

MEF注册类型

MEF使用基于属性的系统来注册类型到容器。其结果是,添加类型到容器很简单:只需要[Export] 特性到需要的类型,如下代码所示。

[Export(typeof(ILoggerFacade))]
public class CallbackLogger: ILoggerFacade
{
}

另一种选择是当使用MEF创建一个类的实例并注册器到容器。代码如下(来自MEF模块化快速入门):

protected override void ConfigureContainer()
{base.ConfigureContainer();// Because we created the CallbackLogger and it needs to // be used immediately, we compose it to satisfy any imports it has.this.Container.ComposeExportedValue<CallbackLogger>(this.callbackLogger);
}

注意:
当使用MEF作为你的容器是,推荐使用特性去注册类型。

解析

在一个类型注册之后,它可以被解析或作为依赖注入。当一个类型被解析,容器需要创建一个新实例,先将依赖项注入到次实例。

一般的,当一个对象解析时,以下三种情况之一会发生:

  • 如果类型没有被注册,容器会抛出一个异常。

    注意
    一些容器,包括Unity,允许你在不注册它的情况下解析一个具体类。
  • 如果类型被注册成单例,容器返回一个单例实例。如果这是该类型第一次被调用,容器创建它并保持它的引用,以便以后调用。
  • 如果类型没用被注册成单例,容器每次都会返回个新值。
    注意
    默认情况下,MEF类型注册为单例,容器保持此单例的引用。在Untiy中,默认情况类型注册不为单例。

Unity解析实例

下面的代码示例展示了命令快速入门中OrdersEditorView 和OrdersToolBar 视图从容器解析的地方,和怎样将他们联系到区域里。

// OrderModule.cs
public class OrderModule : IModule
{public void Initialize(){this.container.RegisterType<IOrdersRepository, OrdersRepository>(new ContainerControlledLifetimeManager());// Show the Orders Editor view in the shell's main region.this.regionManager.RegisterViewWithRegion("MainRegion",() => this.container.Resolve<OrdersEditorView>());// Show the Orders Toolbar view in the shell's toolbar region.this.regionManager.RegisterViewWithRegion("GlobalCommandsRegion",() => this.container.Resolve<OrdersToolBar>());}...
}

OrdersEditorViewModel 的构造器包含下面的依赖项(订单存储库和订单命令代理),这些都是在其被解析时注入的。

// OrdersEditorViewModel.cs
public OrdersEditorViewModel(IOrdersRepository ordersRepository, OrdersCommandProxy commandProxy)
{this.ordersRepository = ordersRepository;this.commandProxy     = commandProxy;// Create dummy order data.this.PopulateOrders();// Initialize a CollectionView for the underlying Orders collection.this.Orders = new ListCollectionView( _orders );// Track the current selection.this.Orders.CurrentChanged += SelectedOrderChanged;this.Orders.MoveCurrentTo(null);
}

除了上面代码展示的构造函数注入,Unity也可以属性注入。任何属性只要有[Dependency] 特性,当对象被解析时可以自动解析并注入。

MEF解析实例

下面代码示例展示Bootstrapper 怎样获得一个壳实例(来自MEF模块快速入门)。不是具体类型的,代码可以需要一个接口的实例(下面不是一个具体类型吗?)。

protected override DependencyObject CreateShell()
{return this.Container.GetExportedValue<Shell>();
}

任何MEF解析的类,你也可以使用构造函数注入,如下代码所示(来自MEF模块化入门),ILoggerFacade和 IModuleTracker 被注入了。

[ImportingConstructor]
public ModuleA(ILoggerFacade logger, IModuleTracker moduleTracker)
{if (logger == null){throw new ArgumentNullException("logger");}if (moduleTracker == null){throw new ArgumentNullException("moduleTracker");}this.logger = logger;this.moduleTracker = moduleTracker;this.moduleTracker.RecordModuleConstructed(WellKnownModuleNames.ModuleA);
}另一个选择是利用属性入门,如下ModuleTracker 所示(来自MEF快速入门),ILoggerFacade 属性被注入。
C#
[Export(typeof(IModuleTracker))]
public class ModuleTracker : IModuleTracker
{[Import] private ILoggerFacade Logger;
}

在Prism中使用依赖注入容器和服务

依赖注入容器,通常简称“容器”,用于满足组件之间的依赖性;满足这些依赖关系通常涉及注册和解析。Prism提供Untiy和MEF支持,但并不是非它俩不可。因为Prism通过IServiceLocator 接口访问容器,所有容器是可以被替换的。未做到这点,你的容器必须实现IServiceLocator 接口。通常,若果你替换容器,你也要提供对应的引导器。IServiceLocator 接口定义在公共服务定位器库。这是一个提供抽象的IoC(控制反转)容器的开源代码,例如依赖组人容器和服务定义器。它的目标是使类库使用控制反转和服务定位模式却不依赖于特别的实现。

Prism库提供UnityServiceLocatorAdapter 和MefServiceLocatorAdapter。这俩适配器都实现了ISeviceLocator 接口并扩展ServiceLocatorImplBaset类型。下图展示了类的继承关系。

公共服务定位器在Prism中的实现

尽管Prism不依赖于特别的容器,但一个程序中只能使用一种容器。(然后是一堆废话,意思是示例中使用了某一容器,不代表Prism就非得用这个容器,也可以使用其他容器,Prism就是这么牛逼,写书的你就这么喜欢玩逻辑游戏吗)。

IServiceLocator

下面代码展示了IServiceLocator 接口。

public interface IServiceLocator : IServiceProvider
{object GetInstance(Type serviceType);object GetInstance(Type serviceType, string key);IEnumerable<object> GetAllInstances(Type serviceType);TService GetInstance<TService>();TService GetInstance<TService>(string key);IEnumerable<TService> GetAllInstances<TService>();
}

Prism中扩展了服务定位器,扩展方法的嗲吗如下所示。你可以看到IServiceLocator 用来解析,这意味着它是用来获取示例的;不是用来注册的。

// ServiceLocatorExtensions
public static class ServiceLocatorExtensions
{public static object TryResolve(this IServiceLocator locator, Type type){try{return locator.GetInstance(type);}catch (ActivationException){return null;}}public static T TryResolve<T>(this IServiceLocator locator) where T: class{return locator.TryResolve(typeof(T)) as T;}
}

TryResolve 扩展方法——Unity 容器不支持——返回一个已经注册的型的实例;否则它返回null。

ModuleInitializer 会用IServiceLocator 在模块加载期间来解析模块,如下代码所示。

// ModuleInitializer.cs - Initialize()
IModule moduleInstance = null;
try
{moduleInstance = this.CreateModule(moduleInfo);moduleInstance.Initialize();
}
...

// ModuleInitializer.cs - CreateModule()
protected virtual IModule CreateModule(string typeName)
{Type moduleType = Type.GetType(typeName);if (moduleType == null){throw new ModuleInitializeException(string.Format(CultureInfo.CurrentCulture, Properties.Resources.FailedToGetType, typeName));}return (IModule)this.serviceLocator.GetInstance(moduleType);
}

使用IServiceLocator注意事项

   IServiceLocator 不是意味着它就是通用容器。容器有不同的用法语义,以决定为什么他被选中。牢记这一点,股票操盘程序使用依赖注入容器直接代替使用IServiceLocator。这是一个开发程序的推荐方式。

在以下情况下,你该考虑使用IServiceLocator

  • 你是个独立软件供应商,需要开发一个第三方服务以支持多个容器。
  • 你要设计一个服务,需要多个容器来组织。

更多信息

有关容器的更多信息,请看以下:

  • MSDN上的Unity Application Block
  • CodePlex上的Unity community site
  • MSDN上的Managed Extensibility Framework Overview
  • CodePlex上的MEF community site
  • Martin Fowler的站点上的文章Inversion of Control containers and the Dependency Injection pattern
  • MSDN杂志上的Design Patterns: Dependency Injection
  • MSDN杂志上的Loosen Up: Tame Your Software Dependencies for More Flexible Apps
  • Castle Project
  • StructureMap
  • Spring.NET
下一篇 | 上一篇 | 主要 | 社区

转载于:https://www.cnblogs.com/DoubleChen/p/3696295.html

3: 组件间的依赖管理(纯汉语版)相关推荐

  1. Prism 文档 第三章 管理组件之间的依赖关系

                                                                          第3章:管理组件之间的依赖关系 基于Prism库的复合应用程 ...

  2. Android组件化下依赖统一管理

    使用Gradle进行依赖统一管理 前言 依赖统一管理使用步骤 第一步:定义versions.gradle文件 第二步:编辑版本内容 第三步:在project的build.gradle进行引用我们新建的 ...

  3. React中组件间通信的方式

    React中组件间通信的方式 React中组件间通信包括父子组件.兄弟组件.隔代组件.非嵌套组件之间通信. Props props适用于父子组件的通信,props以单向数据流的形式可以很好的完成父子组 ...

  4. Maven项目依赖管理 学习笔记

    本学习笔记根据慕课视频[Maven项目依赖管理]摘录,主要学习maven的使用方式,从理念到实践.基础到应用,掌握基于maven管理java项目的方式. 目录 第一章 Maven概述 Maven简介 ...

  5. python 全栈开发,Day91(Vue实例的生命周期,组件间通信之中央事件总线bus,Vue Router,vue-cli 工具)...

    昨日内容回顾 0. 组件注意事项!!!data属性必须是一个函数!1. 注册全局组件Vue.component('组件名',{template: ``})var app = new Vue({el: ...

  6. 容器间通信_Vue组件间通信的6种方式,前端工程师人人都会,网友:太简单了...

    Vue 组件间通信只要指以下 3 类通信:父子组件通信.隔代组件通信.兄弟组件通信,下面分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信. (1)props / $emit 适用 父子组件通 ...

  7. 【Android 组件化】路由组件 ( 组件间共享的服务 )

    文章目录 一.组件间共享的服务 二.注解处理器添加对上述 " 组件间共享的服务 " 的支持 三.注解处理器 生成代码规则 四.完整注解处理器代码 及 生成的 Java 代码 1.注 ...

  8. 持续集成之“依赖管理”

    转自: http://www.infoq.com/cn/news/2011/05/ci-dependency-management http://kb.cnblogs.com/page/101101/ ...

  9. Java-GUI编程实战之管理系统 Day2【Swing(组件介绍、布局管理器、事件类及监听器类)、基础组件按钮和输入框的用法】

    视频.课件.源码[链接:https://pan.baidu.com/s/13ffqGDzH-DZib6-MFViW3Q 提取码:zjxs] Java-GUI编程实战之管理系统 Day1[项目开发流程. ...

  10. Vue 组件间通信六种方式

    前言 组件是 vue.js最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.一般来说,组件可以有以下几种关系: 如上图所示,A 和 B.B 和 C.B 和 D ...

最新文章

  1. %config InlineBackend.figure_format=svg#矢量图设置
  2. SAP 不支持交货单中同一个物料多个行项目HU与序列号组合发货场景
  3. HarmonyOS之AI能力·文字图像超分
  4. UE4学习-AI导航网格的创建及修改
  5. CCAI 2017 | 专访德国语言技术领军者 Hans Uszkoreit:深度学习还不足以解决 NLP 核心问题...
  6. 历史上华人计算机科学家,郑州大学韩家炜、张宏江两位校友在世界顶尖计算机科学家排名中再度名列华人科学家和中国大陆科学家之首...
  7. [转载]未在本地计算机上注册“microsoft.ACE.oledb.12.0”提供程序。
  8. tpc三次握手与四次挥手
  9. 小米9008授权账号_小米AI音箱APP的秘密
  10. 2022年电子商务概论(农)之形考作业二
  11. 2021年高压电工模拟考试题及高压电工操作证考试
  12. vs2003网站项目加载失败解决方案
  13. 视觉SLAM⑧----视觉里程计Ⅱ(光流法与直接法)
  14. unity上线流程_新的Unity商店上线了
  15. 干货!假新闻检测:观察新闻本身,更要观察它所在的新闻环境
  16. 【python数模小作业】动手‘预习‘高数之 人口预测(线性拟合)
  17. freeswitch呼叫中心之freeswitch集成百度MRCP智能语音
  18. thx是什么意思_thx是什么意思?
  19. 使用lorax 构建定制化操作系统
  20. Maven - 客户端 Nexus 配置

热门文章

  1. Android 分享两个你学习android 平台开发必须碰到的几个知识点的组件【天气预报、日期】View 组件...
  2. XMarks 书签王者
  3. Sigar libsigar-amd64-linux.so
  4. 互联网公司忽悠员工的黑话,套路太深了。。
  5. 技术出身要创业,容易吗?
  6. 专科学历 工作机会少的可怜?这个简单方法帮几千人提升3倍工作机会
  7. UC大裁员:全体带薪休假至3月31日,补偿措施羡煞网友!
  8. axios.js post 后台木有数据
  9. Java虚拟机工作原理详解 (一)
  10. android 系统应用在运行时被卸载