zoukankan      html  css  js  c++  java
  • 使用“管道”与“应用程序生命周期”重构:可插拔模块

        本篇博客依然用于总结工作中遇到的较有用的设计模式。

        入正题。

    历史代码


        我目前开发的系统中,要实现以模块的方式进行动态扩展。这些模块是以独立程序集的方式嵌入到系统中。原系统中,使用了一个简单的接口 IModule 来实现模块的初始化:

    public interface IModule
    {
        void Initialize();
    }

    这样,在应用程序初始化时,会检测指定目录 Modules 下的所有程序集,并对其中所有实现 IModule 接口的类型进行初始化调用:

    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
    
            var modules = this.GetAllModules();
            foreach (IModule module in modules)
            {
                module.Initialize();
            }
        }
    }

        这样的方案在早期可以满足一定的需求。但是随着应用程序的逐渐膨胀,越来越多、越来越细的需求,这样的初始化工作已经不能胜任。例如,某个插入的程序集,不仅需要在应用程序初始化时做一些操作,还需要在所有 Module 加载完成后再做一些更多的特殊任务。此时,这样的接口设计已经不能实现这个需求,所以我们需要重构原有的设计,添加新的功能。

        可能您的第一个想法,是在 IModule 接口中加入新的方法,如 ModulesInitialized() ,然后在 foreach 循环结束后再次调用。可是随着需求越来越多,会导致 IModule 接口不断变大。这样,不但得到了一个“胖接口”,而且还破坏了接口的稳定性。

        接下来,看一看我们最终采用的方案:

    新的设计


        重构方案如下,先在底层定义以下接口,表示应用程序的生命周期事件:

    namespace OEA
    {
        /// <summary>
        /// 应用程序生成周期定义
        /// </summary>
        public interface IApp
        {
            /// <summary>
            /// 依赖注入完成
            /// 这里是应用程序的入口开始
            /// </summary>
            event EventHandler DICompleted;
    
            /// <summary>
            /// 所有实体类初始化完成
            /// </summary>
            event EventHandler LibrariesInitialized;
    
            /// <summary>
            /// 所有初始化工作完成
            /// </summary>
            event EventHandler InitializeCompleted;
    
            /// <summary>
            /// 应用程序完全退出
            /// </summary>
            event EventHandler Exit;
        }
    
        /// <summary>
        /// 客户端应用程序生命周期定义
        /// </summary>
        public interface IClientApp : IApp
        {
            /// <summary>
            /// 界面组合完成
            /// </summary>
            event EventHandler Composed;
    
            /// <summary>
            /// 各模块初始化完成
            /// </summary>
            event EventHandler ModulesIntialized;
    
            /// <summary>
            /// 客户化信息初始化完成
            /// </summary>
            event EventHandler AppDefinitionIntialized;
    
            /// <summary>
            /// 登录成功,主窗口开始显示
            /// </summary>
            event EventHandler LoginSuccessed;
    
            /// <summary>
            /// 登录失败,准备退出
            /// </summary>
            event EventHandler LoginFailed;
        }
    
        /// <summary>
        /// 服务端应用程序生命周期定义
        /// </summary>
        public interface IServerApp : IApp { }
    }
    

    接下来,修改模块初始化接口的代码:

    /// <summary>
    /// 模块初始化器
    /// </summary>
    public interface IModule
    {
        /// <summary>
        /// 两个职责:
        /// 1.某个模块的初始化工作
        /// 2.注册 app 的一些事件,进行额外的初始化
        /// </summary>
        /// <param name="app"></param>
        void Initialize(IClientApp app);
    }

    界面层的应用程序类,实现 IClientApp 中所定义的事件:

    public partial class App : Application, IClientApp
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            this.InitCultureInfo();
    
            this.InjectDependency();
            this.ModifyPrivateBinPath();
            this.OnDICompleted();
    
            this.InitializeLibraries();
            this.OnLibrariesInitialized();
    
            this.Compose();
            this.OnComposed();
    
            this.InitializeModules();
            this.OnModulesIntialized();
    
            this.InitAppDefinition();
            this.OnAppDefinitionIntialized();
    
            if (this.TryLogin())
            {
                this.OnLoginSuccessed();
    
                base.OnStartup(e);
    
                this.ShowSplashScreen();
    
                this.ShowMainWindow();
    
                this.SetupAutomaticGC();
            }
            else
            {
                this.OnLoginFailed();
    
                this.Shutdown();
            }
    
            this.OnInitializeCompleted();
        }
    
        protected override void OnExit(ExitEventArgs e)
        {
            base.OnExit(e);
    
            this.OnExit();
        }
    
        #region IClientApp 的事件
    
        public event EventHandler DICompleted;
    
        protected virtual void OnDICompleted()
        {
            var handler = this.DICompleted;
            if (handler != null) handler(this, EventArgs.Empty);
        }
    
        //其它事件...........

    以上代码实现并触发应用程序的整个生命周期各事件。

    那么各模块扩展的代码如何编写呢?我们需要在 IModule 的 Initialize 方法中,监听并处理 IClientApp 的事件,例如:

    [Export(typeof(IModule))]
    public class GIX4Module : IModule
    {
        public void Initialize(IClientApp app)
        {
            this.InitCache(app);
        }
    
        private void InitCache(IClientApp app)
        {
            app.LoginSuccessed += (o, e) =>
            {
                //Define cache
                //Async caching.
            };
        }
    }

    这样,就实现了在登录成功后,进行缓存的定义和初始化。

        其实,这样的编写模式在.NET框架中随处可见。接下来,我将以 ASP.NET 应用程序开发为例,来分析一下在它里面,是如何进行模块化的扩展的。

     

     

    ASP.NET HttpModule 及 管道模式


        在一般的 ASP.NET 程序设计中,我们一般可以通过 HttpModule 和 HttpHandler 来进行扩展(相关内容,可参见《HTTP Handlers and HTTP Modules Overview》及《ASP.NET Application Life Cycle Overview》)。自定义的 HttpModule 模块,都需要实现 IHttpModule 接口,其代码如下:

    public interface IHttpModule
    {
        void Dispose();
        void Init(HttpApplication context);
    }

    也就是说,通过这样一个接口,我们就可以对 ASP.NET 应用程序进行各种扩展。例如,在《Walkthrough: Creating and Registering a Custom HTTP Module》中的示例 HttpModule 代码。在示例中可以看出,我们可以在 Init 接口实现中,监听并进行处理 HttpContext 生命周期各阶段事件,以达到各阶段代码的扩展。

        是不是和之前的代码非常类似? :)

    结束语


        本次的重构,是一种常用的设计模式。它类似于管道与过滤器,但是又不尽相同。它首先定义了整个应用程序的动态运行架构(生命周期);开始运行时,首先动态插入多个独立模块;各模块中再次在应用程序各阶段插入执行代码(监听并处理生命周期各事件);最终实现高灵活度的模块扩展方案。

     

    欢迎转载,转载请注明:

    转载自 胡庆访http://zgynhqf.cnblogs.com/ ]

  • 相关阅读:
    jdbc基础
    DAO模式(单表)
    window对象
    抽象类VS接口
    sql语句
    JS弹框计算
    HBML表单
    【mysql】:mysql性能优化总结
    【java】:多线程面试题
    spring multipart源码分析:
  • 原文地址:https://www.cnblogs.com/zgynhqf/p/1961081.html
Copyright © 2011-2022 走看看