Prism_06_Modules


通常,为了构建一个低耦合、高内聚的应用程序,我们一般选择分层。WPF通过MVVM模式将程序分为 View-ViewModel-Model ,很大程度上消除了业务逻辑和界面元素之间的高耦合,使得开发人员将更多精力放在业务逻辑层面,UI界面可以交给更专业的UI人员。

通常,一个应用程序由不同的业务模块来组合而成,理想情况下,每个业务模块有独立的功能;业务模块之间是低耦合关系的;每个业务模块能够单独来开发测试和部署。这样程序是非常容易扩展、测试和维护,而Prism提供了将程序模块化的功能。

下图是Prism中具有多个模块的程序设计:

IModule

模块是功能和资源的逻辑集合,可以单独开发、测试、部署和集成到应用程序中。每个模块都有一个中心类,负责初始化模块并将其功能集成到应用程序中,该类实现了 IModule 接口。

首先,当模块被加载时,RegisterTypes 首先被调用来注册模块实现的任何服务和功能。

然后,调用 OnInitialized 方法,执行View 的注册或其他初始化代码。

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();
    }

    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();
        regionManager.RegisterViewWithRegion("MyModuleView", typeof(Views.ThisModuleView));
    }
}

ModuleCatalog

ModuleCatalog 保存着程序可以使用的模块信息,本质上是 ModuleInfo 的集合。创建 ModuleInfo 的方法有以下几种:

  • 在代码中注册
  • 在XAML中注册
  • 在配置文件中注册
  • 在磁盘中发现

在代码中注册

protected override void ConfigureModuleCatalog()
{
    Type moduleCType = typeof(ModuleC);
    ModuleCatalog.AddModule(new ModuleInfo()
    {
        ModuleName = moduleCType.Name,
        ModuleType = moduleCType.AssemblyQualifiedName,
        InitializationMode = InitializationMode.OnDemand, // 按需加载
    });
}

还需要使用 Prism 提供的声明属性:

[Module(ModuleName = "ModuleA")]
[ModuleDependency("ModuleD")]
public class ModuleA : IModule
{
}

在XAML中注册

通常 .xaml 文件作为资源添加到 shell 项目中。ModuleCatalog 是由 App 通过调用 CreateFromXaml 方法创建。

<--! ModulesCatalog.xaml -->


    
        
    

    
        
        
            
                ModuleE
            
        
    

    
    

ModuleInfoGroups 方便对同一程序集中的模块进行分组。在 ModuleInfoGroups 中可以定义模块间依赖。

protected override IModuleCatalog CreateModuleCatalog()
{
    return ModuleCatalog.CreateFromXaml(new Uri("/MyProject;component/ModulesCatalog.xaml", UriKind.Relative));
}

配置文件

可以在 App.config 文件中指定模块信息,此时在运行时添加或删除模块变得容易,而无需再重新编译程序。




    
        

即使程序集在全局程序集缓存中或与程序位于同一文件夹中,assemblyFile 属性也是必须的。

还需要覆盖 CreateModuleCatalog 方法:

protected override IModuleCatalog CreateModuleCatalog()
{
    return new ConfigurationModuleCatalog();
}

在目录中发现

Prism 允许将本地目录指定为 WPF 中的模块目录,然后自动扫描指定的文件夹并搜索程序集。要使用这种方法,需要在模块类上使用声明属性来指定模块名称和依赖。

protected override IModuleCatalog CreateModuleCatalog()
{
    return new DirectoryModuleCatalog() {ModulePath = @".\\Modules"};
}
[Module(ModuleName = "ModuleA", OnDemand = true)]
[ModuleDependency("ModuleD")]
public class ModuleA : IModule
{
}

模块间通信

下面是几种松耦合的通信模式,都有各自的优点,通常会结合使用。

  • 事件 - 模块可以订阅其他模块发布的事件来实现通信。但可能会难以维护,特别是要将许多事件编排在一起完成某个任务时。这时考虑共享服务。
  • 共享服务 - 通过公共接口访问的类。通常位于共享程序集并提供系统范围的服务,例如身份验证、日志记录或配置
  • 共享资源 - 比如数据库或一组 Web 服务

我的公众号 HelloPragram