本节导读:

    本节只有一节关键决策,在本节中您将会了解进行模块化应用程序开发前要进行何种准备工作。主要包含了如果将划分模块,如果将模块分配到程序集中,以及在Silverlight中的如何将程序集分配到xap文件中去。最后,通过一个实例说明了模块间是如何通过接口(可能是在共享库中的)达到松耦合连接。

    在本章翻译完后,我会在本章最后一部分补上全部的导读。

 

4.3 关键决策

你需要做的第一个决定就是你是否要使用模块化的解决方案。模块化应用程序的优势都已经在上文中讲述了,但是在得到这些好处前,也必需付出一些东西。如果你选择开发一个模块化解决方案,那么以下内容是需要考虑的:

l 确定你需要使用哪个框架,你可以构建你自己的模块化框架,使用Prism,MEF,或者其它框架。

l 确定如何组织你的解决方案,确定模块的边界以实现模块化结构。哪些模块应该包含在哪些程序集中。你可以使用模块化开发来简化开发过程,比如控制应用程序是如何部署的或者是否支持插件开发的扩展结构。

l 确实如何分割模块,模块可以根据需要进行分割,比如,包含功能区,提供者模块,开发团队和部署需求。

l 确定整个应用程序所有模块都需要使用的核心模块,核心服务的例子有错误报告,验证和授权服务等。

l 如果你使用Prism,确定选择向模块列表注册模块的方法,在WPF中,你可以通过代码,XAML,配置文件,目录扫描的方法。在Silverlight中,可以使用代码和XAML文件的形式注册。

l 确定模块间的通信方法和依赖策略,模块间需要互相通信,并且你需要处理模块间的依赖关系。

l 确定你所要使用依赖注入容器,通常来说,模块化系统开发需要使用依赖注入,控制反转(IoC),或者服务定位器(Service Locator)来保持松耦合关系,动态载入和创建模块。Prism支持使用Unity,MEF和其它依赖注入容器,并且向基于Unity和MEF的应用程序提供支持库。

l 最小化应用程序时间,考虑使用模块的使用时加载或者后台下载来最小化启动时间。

l 确定部署要求,你也需要考虑打算如何部署应用程序。它会影响放入XAP文件中的程序集的数量。你可能也需要划分类似Prism库的共享库以利用Silverlight的程序集缓存。

下文将会说明这些决策的实现细节。

4.3.1 将应用程序划分为模块

当你以模块化的形式开发应用程序时,会将应用程序组织为若干可以独立开发,测试和部署的客户端模块。每个模块都会封装应用程序所有功能中的一部分。你需要做出的第一个设计决策就是如何将应用程序分解到多个离散的模块中去。

每个模块都应该包含一系列相关的关注点并且有负有不同的责任。一个模块可以表示应用程序的垂直切片(vertical slice of application)或者水平服务分层(horizontal service layer)。大型应用程序可能同时拥有这两种类型的模块。

一个以垂直切片形式组织的应用程序

一个以水平分层形式组织的应用程序

一个大型应用程序中包含的模块的组织方式可能包含以垂直切片方式和水平分层两种形式。它们可能包含以下模块:

l 包含特定功能的模块,如在the Stock Trader Reference Implementation (Stock Trader RI)中的新闻模块。

l 包含子系统或者相关用户用例的模块,比如采购,开发票,做总账。

l 包含了系统的基础服务,比如日志,缓存,授权服务,或者web service。

l 包含调用其它业务支撑系统的服务,比如客户关系管理和企业信息管理,和除此本身之外的其它内部系统。

模块间的依赖关系应该最小化。当一个模块依赖于其它模块时,它们之间应该是通过位于共享库的接口进行松耦合,而不是使用具体的类,通过EventAggregator事件类型于其它模块通信。

4.3.2 将模块分配到项目中去

模块有许多种创建和封装的方法。最常用也是推荐的方法是将每个模块都分配到独立的程序集中。这样有助于保证模块间的逻辑隔离也提高了封装性。同样也可以就和如何部署模块一样方便的区分模块的边界。当然,一个程序集包含多个模块也未尝不可,在一些情况下,它还有助于减少解决方案中的项目数量。在一些大型应用程序中,包含10-50个模块也没什么不正常的。这时,将每个模块分配到一个独立项目中然后再全部加到解决方案中去就会影响Visual Studio的性能。一些情况下,如果你坚持将每个模块都分配到独立的程序集或者Visual Studio项目中去,那就可以考虑打散一些模块或者将一些模块放到一个独立的解决方案中去管理。

4.3.3 XAP和模块分解

在Silverlight应用程序中,每个模块通常都放到独立的xap文件中去,当然在一些情况下,也可能在一个xap文件中封装多个模块。你应该考虑应用程序启动或者一个新功能加载时需要多少xap文件支持以保证每次的下载量最少。如果你选择将每个模块都分到独立的项目/程序集,那就需要决定部署时每个xap文件中所包含的程序集数目。

以下是一些将会影响每个xap集中的程序集数量的因数:

l 下载大小和共享依赖,每个xap文件都有一些额外信息比如mainfest(文件列表)和.zip文件封包内容。当然,如果有一些无法的依赖模块或者缓冲库的话,那么每个xap文件中也可能包含这些依赖库,这些都会增加需要下载文件大小。

l 模块被应用程序调用的时机,如果多个模块在同一时间被应用程序调用,比如在应用程序开始的时候显示视图,那么将它们封装在同一个xap文件里以保证它们能够更快的被下载,也保证它们可以在同一时间被应用程序使用。Prism的模块化功能可以保证所有依赖即使在不同的xap文件中也可以以一个正确的顺序被加载。但是就算是在下载大小完全相同的情况下,下载一个文件的性能总是比下载两个文件要好一些。

l 模块版本,如果不同的模块会在不同的时间点被开发和独立的部署,你肯定会将它们放到不同的xap文件中所以使用模块版本划分会更加清晰,也有助于独立的升级。

为了防止同一个模块在不同的xap文件被下载多次,可以使用以下两种途径:

l 将共享依赖分配到一个独立的基础模块中去,并且让其它模块都依赖于它。

l 使用Silverlight的Assembly Library Caching将通用类型放到一个共享库中,这样它就会下载一次并且被Silverlight缓冲而不是Prism的模块下载器缓冲。

4.3.4 使用依赖注入保证松耦合

模块可能依赖于宿主应用程序或者其它模块提供的组件和服务。Prism拥有模块间的组件注册依赖的能力以便他们可以以一个正确的顺序初始化。Prism也支持模块在载入应用程序时初始化。在模块初始化时,模块将会检索自己所需要的额外组件和额外服务,和/或向容器注册组件和服务以便于其它模块调用。

依赖注入的例子

在这个例子中,OrdersModule程序集定义了OdersRespository类(同其它视图和接口一起实现订单的功能)。CustomersModule程序集定义了一个依赖于OdersRespositoryCustomersViewModel 类,通常依赖是基于一个服务公开的接口。应用程序启动和初始化过程将包含以下过程:

1、启动器开始模块初始化过程,模块加载器加载并初始化OrdersModule

2、在初始化OrdersModule时,向容器注册OdersRespository

3、接下来模块加载器加载CustomersModule,模块启动顺序可以由模块元数据中的加载关系所指定。

4、CustomersModule由容器解析以初始化一个CustomersViewModel的实例。而CustomersViewModel有一个通过构造函数注入或者属性注入的基于OdersRespository(通常是通过它的接口)的依赖。于是OrdersModule中注册的实际类型就被容器当作依赖注入到视图模型中。连接的结果就是CustomersViewModelOdersRespository在没有通过紧耦合连接的情况下通过接口引用了。

【注意】:用于暴露OrderRespository (IOrderRepository)的接口可以存在于一个只包含用以提供这些服务的接口的“共享服务”程序集或者 “订单服务”程序集中。这样CustomersViewModelOdersRespository之间就不存在强依赖的关系了。

注意,所有模块与依赖注入容器有隐式依赖关系。该依赖在模块初始化时就被模块加载器注入。

posted on 2011-12-09 21:08  NNKOOK  阅读(216)  评论(0编辑  收藏  举报