.NET责任链模式、单例模式、模板方法模式混用

前言

  哇,看到题目挺长的,这个组合型的东西,到底能干啥呢?本篇文章来一起琢磨琢磨,这两天为了团队的软件赶工,我负责的那一块叫:插件管理器。我们团队的成员用的语言还是挺分散的,本人C#,队长VB.NET,还有其他成员写易语言等,系统的功能插件是我们分开写的,各自用各自的喜欢的语言写各个功能模块的插件,最后用我开发的插件管理器把所有的插件整合到一起。这让我很头疼啊,一个C#版的插件,一个VB.NET版的插件,一个易语言的插件,如果有新成员加入,又来个Python版的插件,叫我如何是好。最普通、最烂的处理方法就是:写很多版本的读取器,然后使用if来根据插件语言使用对应该版本的读取器读取信息,哇,这如果来十几种语言,岂不是坑爹。可能有人会说:引入Kernel32能解决非.NET语言的插件读取吧。确实目前使用了这种方案,能兼容易语言,.NET语言就使用.NET的插件Dll读取方式,但是还不确定Kernel32能不能解决任何语言的插件。所以为了让自己有个后路,我结合题目所说的三种设计模式,写了一个模板,下面看这个模板能干嘛,为什么要这样用。如果有地方使用得不恰当的,希望各位朋友们强拍,我会努力学习改正,如果觉得可以的,点个推荐,谢谢~

框架展示与说明

                                       

  大家看下类图,下面分别介绍各个类的职责。Plugin类是插件的数据结构类,PluginType是一个枚举,它的内容是定义了所有插件的类型,例如DotNet,Python等,扩展的时候需要修改该枚举。然后剩下的就是本章的重点了,用户代码Client使用IPluginAnalyzerable接口来读取插件信息,该接口有两个实现类,一个是PluginAnalyzer(抽象类,定义各个语言的读取器的公共部分),该类实现模板方法模式和责任链模式,让处理命令能在其子类之间互相推卸责任,其子类目前有两个,分别是DotNet,Python的具体读取器。最后一个类ComponentAnalyzer担任封装职责,将责任链初始化好,自己实现单例模式,提供给用户代码调用。所以,最后的效果是:获取ComponentAnalyzer的实例,调用其中一个方法,该方法调用具体读取器链,最后哪个读取器处理了请求,用户是不需要知道的,大概思路就是这样。

实现过程

  本实现过程只是模板的实现,具体应用到项目中还需要做出相应的改变。

  事不宜迟,我们先实现Plugin类和PlugType枚举:

    Plugin:

  1. class Plugin
  2. {
  3. public Plugin(PluginType type,String pluginPath)
  4. {
  5. this.BelongType = type;
  6. this.PluginPath = pluginPath;
  7. }
  8. public PluginType BelongType { set; get; }
  9. public String PluginPath { set; get; }
  10. }

    PlugType:

  1. enum PluginType
  2. {
  3. DotNet=,
  4. Python=,
  5. }

  实现完两个基本的类型以后,然后就来看下本篇的重头戏,我会边贴代码边附加上帮助理解的说明。首先从最底层的IPluginAnalyzerable开始:

  1. interface IPluginAnalyzerable
  2. {
  3. void Analyze(Plugin plugin);
  4. }

  用户代码就是使用该接口的Analyze方法来处理传入的插件的,再下一层有两个类,一个是抽象类PluginAnalyzer,一个是ComponentAnalyzer。

  1. abstract class PluginAnalyzer:IPluginAnalyzerable
  2. {
  3. public PluginAnalyzer(PluginType type)
  4. {
  5. this.analyzerType = type;
  6. }
  7. protected PluginType analyzerType;
  8.  
  9. public void Analyze(Plugin plugin)
  10. {
  11. if (plugin.BelongType == this.analyzerType)
  12. {
  13. String author = GetAuthor(plugin);
  14. String version = GetVersion(plugin);
  15. Console.WriteLine(String.Format("\r\n分析者:{0},插件类型:{1} \r\n{2}\r\n{3}",
  16. this.GetType().Name,plugin.BelongType,author, version));
  17. }
  18. else
  19. {
  20. if (nextAnalyzer != null)
  21. {
  22. nextAnalyzer.Analyze(plugin);
  23. }
  24. }
  25. }
  26.  
  27. private PluginAnalyzer nextAnalyzer;
  28. public PluginAnalyzer NextAnalyzer
  29. {
  30. set
  31. {
  32. this.nextAnalyzer = value;
  33. }
  34. get
  35. {
  36. return this.nextAnalyzer;
  37. }
  38. }
  39.  
  40. protected abstract String GetAuthor(Plugin plugin);
  41. protected abstract String GetVersion(Plugin plugin);
  42. }

  解读:

  每一个继承本类的具体类都有两个字段

    1:所属类型(PluginType枚举),变量名为analyzerType。

    2:下一个分析者(PluginAnalyzer),也就是兄弟类(同样继承PluginAnalyzer)。

  每一个继承本类的具体类都需要重写两个方法GetAuthor和GetVersion,这两个方法将会在模板方法Analyzer内部被使用。

  模板方法Analyzer首先判断传进来的插件类型是否和自身可以处理的类型相同,如果相同则调用自身的方法处理,如果不同则把处理权推给自己的下一位分析者。这样就完成了具体架构的搭建了。

  

  然后就是具体读取器类了,各自有各自的处理相同任务的方式。都继承PluginAnalyzer

    DotNetPluginAnalyzer(该类是处理.NET插件的读取器)

  1. class DotNetPluginAnalyzer:PluginAnalyzer
  2. {
  3. public DotNetPluginAnalyzer(PluginType type)
  4. : base(type)
  5. { }
  6. public DotNetPluginAnalyzer()
  7. : base(PluginType.DotNet)
  8. { }
  9. protected override string GetAuthor(Plugin plugin)
  10. {
  11. return "DotNet的插件,作者名为:Jarvin";
  12. }
  13.  
  14. protected override string GetVersion(Plugin plugin)
  15. {
  16. return "DotNet的插件,版本号为:!!!V2014!!";
  17. }
  18. }

    PythonPluginAnalyzer(该类是处理Python插件的读取器)

  1. class PythonPluginAnalyzer:PluginAnalyzer
  2. {
  3. public PythonPluginAnalyzer(PluginType type)
  4. : base(type)
  5. { }
  6. public PythonPluginAnalyzer()
  7. : base(PluginType.Python)
  8. { }
  9. protected override string GetAuthor(Plugin plugin)
  10. {
  11. return "Python的插件,作者名为:Joker";
  12. }
  13.  
  14. protected override string GetVersion(Plugin plugin)
  15. {
  16. return "Python的插件,版本号为:V---很奇怪----";
  17. }
  18. }

  好了,如何使用?我将新建一个类,把这些读取器包装起来,并且形成一条链,提供一个统一的接口给用户代码调用,下面看我如何包装的。

    ComponentAnalyzeclass ComponentAnalyzer:IPluginAnalyzerable    {

  1. private ComponentAnalyzer()
  2. {
  3. rootAnalyzer = new DotNetPluginAnalyzer();
  4. PythonPluginAnalyzer pythonAnalyzer = new PythonPluginAnalyzer();
  5. rootAnalyzer.NextAnalyzer = pythonAnalyzer;
  6. }
  7. #region 单例模式实现
         private ComponentAnalyzer()
         {
  8.      }
  9. public static ComponentAnalyzer GetInstance()
  10. {
  11. return SingleHelper.GetInstance();
  12. }
  13. private class SingleHelper
  14. {
  15. private static ComponentAnalyzer me = new ComponentAnalyzer();
  16. public static ComponentAnalyzer GetInstance()
  17. {
  18. return me;
  19. }
  20. }
  21. #endregion
  22. PluginAnalyzer rootAnalyzer;
  23. public void Analyze(Plugin plugin)
  24. {
  25. rootAnalyzer.Analyze(plugin);
  26. }
  27. }

  很简单的一个类,我们先看构造函数:把所有语言的读取器连接起来,然后链头是rootAnalyzer,我们每次调用Analyze方法都会调用rootAnalyzer对应的方法,让其在内部传递。弄到这里,差不多完成了,大家可以在最下面直接下载源码运行看结果,下面给出客户端测试类。

  1. class Programe
  2. {
  3. public static void Main(string[] args)
  4. {
  5. IPluginAnalyzerable dotnetAnalyzer=ComponentAnalyzer.GetInstance();
  6. Console.WriteLine("输入Q退出");
  7. while(true)
  8. {
  9. Plugin plugin = RandomPlugin();
  10. dotnetAnalyzer.Analyze(plugin);
  11. if (Console.ReadLine().ToUpper() == "Q")
  12. {
  13. break;
  14. }
  15. }
  16. }
  17. private static Plugin RandomPlugin()
  18. {
  19. Random random = new Random();
  20. PluginType type = (PluginType)random.Next(, );
  21. String plaginPath = Path.GetRandomFileName();
  22. Plugin result = new Plugin(type, plaginPath);
  23. return result;
  24. }
  25. }

   测试结果:看,以一致的方式执行,但是会得到不一样的效果,责任被推到合适的地方做出相应的处理。

  到这里有人会说,那如何证明该模型的扩展性?? 好,下面我扩展一种语言读取器,看我改了多少,对系统影响了多少?

扩展性测试

   我以Ruby为例。

    1.在PluginType中添加一个枚举内容Ruby:

    2.添加一个Ruby读取器:

    3.在ComponentAnalyzer(前面说的包装器)中,把该读取器添加到链条上!

    

    为了测试,在客户端代码中修改Random随机生成数,使其能生成3,大功告成!(这是测试相关,我们没有修改实际客户端任何代码)

     顺利扩展!!!我们修改的只是上层的代码,对于底层,也提供了扩展点,符合对修改封闭,对扩展开放。

总结

  完成了,大家也累了,希望有不对的地方大家大力拍,面向组合编程,面向接口编程,不要面向具体实现类编程,这是我学习设计模式感受最深的一句话。谢谢大家观看。下面提供完整源码。

完整Demo下载

.NET责任链模式(混合单例模式,模板方法模式)-----制作与扩展能力验证的更多相关文章

  1. Tomcat系列(10)——Tomcat主要设计模式5种(外观,责任链,观察者,模板方法,命令模式)

    核心部分 外观模式: RequestFacade应用门面模式(facade)来封装HttpServletRequest. 观察者模式: 事件监听机制,控制组件生命周期的 Lifecycle .Serv ...

  2. (转)《JAVA与模式》之模板方法模式

    该文章转自:http://www.cnblogs.com/java-my-life/archive/2012/05/14/2495235.html 在阎宏博士的<JAVA与模式>一书中开头 ...

  3. 《JAVA与模式》之模板方法模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述模板方法(Template Method)模式的: 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式 ...

  4. 十一个行为模式之责任链模式(Responsible Chain Pattern)

    定义: 将具有相同接口的责任类串行在一起,解耦请求的发送者和处理者.沿着这条链进行请求,直到有对象处理它为止. 结构图: Handler:抽象处理类,定义了所有责任类处理方法的接口,不同的处理方法在子 ...

  5. java责任链模式及项目实际运用

    1.前言 上次我们认识了java责任链模式的设计,那么接下来将给大家展示责任链模式项目中的实际运用.如何快速搭建责任链模式的项目中运用. 2.简单技术准备 我们要在项目中使用借助这样的几个知识的组合运 ...

  6. 重学 Java 设计模式:实战责任链模式「模拟618电商大促期间,项目上线流程多级负责人审批场景」

    作者:小傅哥 博客:https://bugstack.cn - 原创系列专题文章 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 场地和场景的重要性 射击

  7. JAVA模板方法模式

    模板方法模式的结构 模板方法模式是所有模式中最为常见的几个模式之一,是基于继承的代码复用的基本技术. 模板方法模式需要开发抽象类和具体子类的设计师之间的协作.一个设计师负责给出一个算法的轮廓和骨架,另 ...

  8. 设计模式(22)--Template Method(模板方法模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义: 模板方法模式是类的行为模式.准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声 ...

  9. 《Java设计模式》之模板方法模式

    模板方法模式是类的行为模式.准备一个抽象类.将部分逻辑以详细方法以及详细构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类能够以不同的方式实现这些抽象方法,从而对剩余的逻辑有 ...

随机推荐

  1. 在安卓3.0以下版本使用Fragment的注意事项

    1. 按照网上的Fragment官网资料翻译来做一直有错: 10-03 02:43:13.971: E/AndroidRuntime(1921): java.lang.RuntimeException ...

  2. gameObject, vector and transform

    调用其它组件中成员 通过GameObject(游戏物体). Base class for all entities in Unity scenes.  是Unity场景里面所有实体的基类. 可以理解为 ...

  3. UITableView的常用属性和cell的内存优化

    UITableView的常用属性: 分割线颜色设置: 1> 设置separatorStyle: 分割线的颜色 方法:tableView.separatorStyle = UITableViewC ...

  4. 转载:js动态获取图片长宽尺寸(兼容所有浏览器,速度极快)

    转自:http://blog.phpdr.net/js-get-image-size.html lightbox类效果为了让图片居中显示而使用预加载,需要等待完全加载完毕才能显示,体验不佳(如fili ...

  5. SQL Server数据库与max degree of parallelism参数

    我们今天主要向大家讲述的是SQL Server数据库中的max degree of parallelism参数,当 SQL Server 数据库在具N个微处理器或是 CPU 的计算机上运行时,它将为每 ...

  6. 请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

    // test20.cpp : 定义控制台应用程序的入口点. // #include "stdafx.h" #include<iostream> #include< ...

  7. BZOJ 2653 middle

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=2653 题目大意:多组询问,求左右端点在规定范围内移动所能得到的最大中位数. [分析] 求中 ...

  8. yebis error ---depth of field

    前几天在墙外无法登陆cnblogs...导致很多blogs就没写了 有几篇比较值得记下来的,但是我已经不记得了,应该和sao有关scalable ambient obscurance 我似乎回忆起一点 ...

  9. IIS Express 及 vs2008下使用IIS Express

    介绍 IIS Express 开发 ASP.NET 的应用程序是我的主要工作.当然我会选择最适合的开发环境.客户多属于企业用户,我的开发的选择,多半是 ASP.NET Web Application ...

  10. SQLite中的日期基础

    SQLite包含了如下时间/日期函数: datetime().......................产生日期和时间 date()...........................产生日期 t ...