使用Prism库开发模块化应用程序
核心概念
本节介绍Prism中与模块化相关的核心概念,包括IModule
接口、模块加载过程、模块目录、模块之间的通信以及依赖注入容器。
IModule: 模块化应用程序的构建块
一个模块是功能和资源的逻辑集合,以一种可以单独开发、测试、部署和集成到应用程序中的方式打包。一个包可以是一个或多个程序集。每个模块都有一个负责初始化模块并将其功能集成到应用程序中的中心类。该类实现了IModule
接口。
注意: 当包中存在一个实现IModule
接口的类时就足以将该包识别为模块。
IModule
接口有两个方法,分别命名为OnInitialized
和RegisterTypes
。这两个方法都需要引用一个依赖注入容器对象作为参数。当一个模块被加载到应用程序中时,首先会调用RegisterTypes
方法,您应该在此方法中注册该模块所有服务或该模块实现的功能。接着调用OnInitialized
方法,在此方法中您应执行像视图注册或所有其他关联模块的初始化等任务。
public class MyModule : IModule
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
// register with the container that SomeService implements ISomeService
// ISomeService is defined in the Infrastructure module, see app architecture diagram
containerRegistry.Register<MyApplication.Infrastructure.ISomeService, SomeService>();
}
public void OnInitialized(IContainerProvider containerProvider)
{
// use the containerProvider to retrieve the instance of the Prism RegionManager
// and register the view in this module with a specific region in the app
var regionManager = containerProvider.Resolve<IRegionManager>();
regionManager.RegisterViewWithRegion("MyModuleView", typeof(Views.ThisModuleView));
}
}
模块生命周期
Prism中的模块加载过程包括以下顺序:
- 注册 模块是通过在类内部实现IModule接口进行创建的。
- 发现模块。 在运行时为特定应用程序加载的模块需在模块目录中定义。该目录中包含有关要加载的模块的信息,例如它们的位置以及它们的加载顺序。
- 加载模块。 包含模块的程序集会被加载到内存中。
- 初始化模块。 然后初始化模块。这意味着创建模块类的实例,并且通过
IModule
接口调用它们的RegisterTypes
和OnInitialized
方法。
模块目录
ModuleCatalog
中包含有应用程序中可以使用的模块的信息。模块目录本质上是ModuleInfo
类的集合。每个模块都在一个ModuleInfo
类中进行描述,该类会记录模块的名称、类型、位置以及其他属性。有几种典型的方法可以用ModuleInfo实例来填充ModuleCatalog:
- 在代码中注册模块
- 在XAML中注册模块
- 在配置文件中注册模块
- 在磁盘上的本地目录中发现模块
您应该使用的注册和发现的机制取决于您的应用程序需要什么。使用配置文件或XAML文件可以让您的应用程序不需要对模块的进行引用。使用目录则可以让您的应用程序在发现模块时无需在文件中指定它们。
控制何时加载模块
Prism应用程序可以尽快初始化模块,即“可用时”,也可以在应用程序需要时再初始化,即“按需”。请考虑以下加载模块的指导原则:
- 应用程序运行所需的模块必须随应用程序加载并在应用程序运行时初始化。
- 包含很少使用的功能(或是其他模块可选依赖的支持模块)的模块可以按需加载和初始化。 考虑如何划分您的应用程序、常见使用场景以及应用程序启动时间,以确定如何配置您的应用程序进行初始化
在模块与应用程序进行集成
Prism.DryIoc.[Platform]
和Prism.Unity.[Platform]
程序集都提供了一个基于Application
的类,其可用作App类的基类。您可以重写虚方法CreateModuleCatalog来创建所需类型的模块目录。
对于应用程序中的每个模块,您需实现IModuleInfo
接口以注册模块类型和服务。当将模块集成到应用程序时,通常会执行以下常见操作:
- 将模块的视图添加到应用程序的导航结构中。当构建复合UI应用程序时使用视图发现或视图注入是常用的操作。
- 订阅应用程序级别的事件或服务。
- 向应用程序的依赖注入容器注册共享服务。
模块间的通信
尽管模块之间应该具有低耦合性,但是模块之间进行通信还是很常见的。目前有几种松散耦合的通信模式,每种都有自己的优点。通常这些模式的会被组合使用用于创建最终的解决方案。以下是其中的一些模式:
- 松散耦合事件。一个模块可以广播某个事件已经发生。其他模块可以订阅这些事件,以便在事件发生时得到通知。松散耦合的事件在两个模块之间建立通信是一种轻量级的方式,因此它们很容易实现。然而如果设计过度依赖事件,则可能会使应用程序很难维护,特别是需要协调许多事件来完成单个任务的时候。在这种情况下最好考虑使用共享服务。
- 共享服务。共享服务是通过公共接口访问的类。通常共享服务可以在共享程序集中找到,并提供系统范围的服务,例如身份验证、日志记录或配置。
- 共享资源。如果您不希望模块直接彼此通信,也可以通过共享资源(如数据库或一组Web服务)间接通信。
依赖注入和模块化应用程序
Unity和DryIoc等容器允许您轻松使用控制反转 (IoC) 和依赖注入,它们都是强大的设计模式,有助于以松散耦合的方式组合组件。它允许组件获取对它们所依赖的其他组件的引用,而不必对这些引用进行硬编码,从而促进更好的代码重用和改进的灵活性。在构建松散耦合的模块化应用程序时,依赖注入会非常有用。Prism也被设计为与用于在应用程序中组合组件的依赖注入容器无关。
无论选择三个容器中的哪一个,Prism都将使用该容器来构造和初始化每个模块,以便它们保持松散耦合。