Prism允许你选择其中的任何部分进行使用。你可选取使用Model-View-ViewModel (MVVM) 模式、模块性(modularity)、 区域(regions)、命令(commands)、或者事件( events)等任意的组合。但是如果你致力于低耦合并想获得Prism的全部好处 user interface (UI) 分离模式功能,你可以参照一个规范的架构方式来设计你的UI程序。此架构可以指导你如何组织你的解决方案,此外,还可以享用到超越于仅仅使用Prism类 库功能之外的包括更多抽象和模式,这些都可以应用到你的程序中。
解决方案文件夹结构
典型的Prism程序有多个工程项目。包括主UI 应用程序项目, 一个 Silverlight 应用程序项目或者一个WPF应用程序项目。称为外观程序或外观项目, 当你使用到Prism模块性功能时,也会包含外观,你还会有一个或多个模块项目。 此外,典型的项目会有一个或多个包含了一些公用的类型类库项目,这些公用类库项目被外观项目和模块项目共享。下图展示了Stock Trader Reference Implementation (Stock Trader RI)的解决方案的组织结构。 The Stock Trader RI包含了基于WPF (Desktop) 和Silverlight两种平台的实现,图中没有展开WPF桌面应用的文件夹。你可以参看Silverlight版本的文件夹组织结构 (StockTradeRI.Silverlight,包括四个模块项目(市场、新闻、职位和观察)以及一个公用的类库项目 (StockTraderRI.Infrastructure.Silverlight)。
Stock Trader RI 解决方案文件夹组织结构图
Prism类库
除此之外,大多数项目都需要应用Prism 类库的程序集。Prism类库包含三个程序集。你可以引用编译好的dll或者把源码项目添加到你的解决方案中并进行项目引用。后者的好处是可以通过 GoTo的方式查看Prism源码类型等,同时可以作为你项目的一部分对Prism 类库程序集进行强名称签名。
启动引导器(Bootstrapper)
Bootstrapper对应于构建一个使用 Prism的应用程序初始化过程。通过bootstrapper,你可以集中初始化你的一些程序代码;你还可以更好的控制Prism类库组件连接到你的应 用程序。例如, 如果你有一个已经存在的程序要添加Prism类库到其中,那么你可以在程序运行之后初始化启动引导进程(bootstrapping process)。
在传统的WPF程序中,通过在App.xaml 文件中指定Uniform Resource Identifier (URI)的方式来启动主窗体,在传统的Silverlight程序中,需要在App.xaml.cs的code behind文件中设置RootVisual。用Prism类库创建的程序,通过bootstrapper的职责来启动外观或主窗体。这是因为外观的启动 依赖于Prism的一些服务,如区域管理器和时间聚合器需要在外观显示之前进行注册。
Prism类库包含两个抽象的bootstrapper 类,UnityBootstrapper用于整体容器(Unity Container),MefBootstrapper 用于MEF(Managed Extensibility Framework),均用于处理初始化过程。 UnityBootstrapper 类中的许多方法都是虚方法。你可以重载这些继承于基类的虚方法来实现你的自定义bootstrapper。如果你使用的容器不是 Unity,那么你可以自己写一个针对这个特别的容器的bootstrapper。
Bootstrapper 实现了一个工作流程来初始化Prism程序。包含了创建和初始化容器、区域适配器映射region adapter mappings、外观和模块。下图显示了Bootstrapper启动过程中的几个阶段。
启动引导过程
容器(Container)
依赖注入容器用于解决组件间的依赖;解决依赖通常使容器知道其中的类型和实例,用于解决依赖性 (registration) ,并且获取这些引用依赖到一个消费类中 (resolution)。Prism类库支持Managed Extensibility Framework and (MEF) 、Unity Application Block (Unity) 容器、但它并不是一种特有的容器。 因为类库通过IServiceLocator 接口访问容器。容器可以被替换。要使用别的容器,只要实现IServiceLocator 接口并暴露你的容器API来注册和resolution。而且,如果你要更换容器,你必须实现你自己特定的bootstrapper。 IServiceLocator 接口定义于 Common Service Locator 类库中。这是一个开源的成果,实现了 IoC (Inversion of Control) 抽象的容器,如依赖注入容器,和服务定位器。使用该类库的目的是协同IoC 和Service Locator。
Prism类库提供了MefServiceLocatorAdapter 和UnityServiceLocatorAdapter。MefBootstrapper 类和UnityBootstrapper 类注册了它们各自基于IServiceLocator实现,当需要依赖注入时会使用需要的容器,只要通过 Prism类库代码,就可以保证Prism类库和使用的容器解耦。
对自行开发的代码来说,你可以简单的基于你选择的容器的API进行代码开发。 你不必通过IServiceLocator 抽象,除非你想你的代码对容器不可知。比如,Prism 引用指南和参考实现演示了不基于MEF 和Unity 容器直接实现的API。
外观(Shell)
外观是指应用程序的主窗体或根UI(Root UI)元素,主窗体包括 UI 相关的内容。 在需要的情况下, 应用程序外观可以由多个窗体构成,但大多数情况下都只有一个包含了多个视图的一个窗体。在Silverlight应用程序中,则考虑RootVisual 元素。外观中可能包括被称为区域(Region)的元素,在区域中可报刊多个视图元素来进行展现。外观也可由一个顶级的UI元素构成,比如一个主菜单和工 具栏。 外观shell定义了整个应用程序整体的视觉样式。可定义风格和边框在外观Shell的布局中进行展现,还可以定义风格、模板和主题,然后应用到视图中并 插入到外观Shell中。
Prism应用程序中,外观Shell 无需了解应用程序的功能。它只提供内容展现,所以被称为“外观Shell”。
模块Module
模块Module是Prism应用程序中的一个功能单元。恰当设计的模块可以与整个应用程序和其他的模块解耦。通过把应用程序拆分成模块,你可以提升你隔离模块的技能,安装不同的功能集交付不同版本的应用程序,添加新模块来扩展你的应用程序功能。
模块定义的方式可以让其在应用程序运行时发现和加载模块。模块可以与其他模块或访问服务以松耦合的方式进行通信。这种方式减少了维护、添加或移除系统功能时的冲突。模块设计也可以帮助测试和部署。
模块的通常用法是代表着不同的功能集合。例如:
- 模块包含一个特定的程序功能,如新闻功能。
- 模块包含一个特定的子系统,如采购、支付或结账系统。
- 模块包含了基础服务,如日志和授权服务或Web服务等。
- 模块包含调用 line-of-business (LOB) 系统的服务,如 Siebel CRM 和SAP,以作为内部系统的补充。
下面的示例 Stock Trader RI中的四个模块:
- 职位。 该模块用于显示职位和处理买/卖的订单。
- 市场。该模块用于处理统计和现实投资组合的趋势信息。
- 观察。该模块处理观察列表,用于显示用户关注的基金列表。同时处理列表内容的增删。
- 新闻。该模块用于用户投资相关的新闻聚集和显示。
当你模块化开发你的程序时,你应当把程序分割成独立的模块由不同的团队进行开发、测试和部署。
视图和视图模型Views and ViewModels
Views是包含外观窗体的 UI的组成部分。 你可以把它看作是呈现在客户端的用户控件。实际上,Prism 类库不必定义成用户控件。你可以使用数据模板来定义一个视图以呈现模型数据。view之间可以通过WPF的能力自由呈现界面。简单来说,View是UI元 素的定义了UI呈现部分的集合,它提供了你UI上各独立部分的一种封装形式。
Model-View-ViewModel (MVVM) 模式已经 成为WPF/Silverlight展示模式中最常用的模式。使用MVVM模式时, 视图View与视图模型View model在运行时相联系并提供了数据和交互逻辑来支持视图View的展现。
控制器Controllers
你的程序代码的另一个重要职责是管理视图Views。你要控制逻辑来创建视图View实例并初始化,然后在外观Shell中展现,或者在适当的时候 组合视图、切换视图等。用户交互动作驱动切换和展现视图的逻辑,也可能包括后台发生的逻辑。应用程序控制器Controller模式定义了这种指责的抽 象。在快速指南 和Stock Trader RI示例中,你可以看到控制器Controller的例子和控制器Controller的实现。
服务Services
在复合应用程序的上下文中,服务指的是一套公用的功能集合,可以通过服务定位器Service Locator模式来进行访问。简单的说,它只是提供了你应用程序中一些功能性的类。 实际上,不要自己构造一个类的实例,你需要使用服务定位器Service Locator模式来获取类实例的引用。 典型的,你需要获取一个接口引用而不要一个具体的类型引用,这样消费端代码就可以与实现接口的具体类型解耦。获取引用之后,你就可以使用服务来使用相应功 能。
下面是几个服务Services的示例:
- 基础服务Infrastructure services。区域管理器Region Manager和事件聚合器Event Aggregator就是基础服务的示例,它提供了架构性支持,来实现应用程序和部件间通信。你需要写的其他的基础性服务 可能包括一个缓存服务或者一个授权认证服务。
- 数据服务Data services。一般来说, 服务被用作一个外观façade,表现在你的视图模型view models 和控制器controllers 以及数据源之间。然后进入到展示层。数据可能被直接从磁盘、数据库或者一个Web服务中获取。数据服务避免了显示逻辑和获取要显示的数据逻辑之间的耦合。
通常,上下文中的服务是一个进程内的服务。 它也可能是进程外的服务的打包调用然后作为一个Web服务。但它通常也可能是内存空间中的一个对象作为一个客户端,这样就能通过服务接口和服务定位器Service Locator模式来解耦。
小结
一个典型的Prism应用程序通过架构性的概念来协调各个独立部分。下图展示了这种示例型的应用程序架构。
示例Prism应用程序架构
在这个示例中,应用程序创建了引导启动器Bootstrapper并启动。引导启动器Bootstrapper创建并展现外观Shell。模块A加 载并注入三个视图Views到外观Shell中。每个视图View后台都有一个视图模型View model来支持。前两个视图可能在区域Region中呈现出来, 控制器Controller从服务中获取数据来创建并初始化它们。第三个视图中,它只展现一次并且不需要改变,所以没有包括控制器,但它仍然有视图模型 View Model来进行支持,并且视图模型通过服务来管理它自己的数据。图中没有详述的模块类、视图模型View Model、控制器Controller和服务等对象,它们全通过容器进行创建,这样所有的依赖都可以被注入到容器中。