.NET責任鏈模式(混合單例模式,模板方法模式)-----製做與擴展能力驗證

.NET責任鏈模式、單例模式、模板方法模式混用

 

前言

  哇,看到題目挺長的,這個組合型的東西,到底能幹啥呢?本篇文章來一塊兒琢磨琢磨,這兩天爲了團隊的軟件趕工,我負責的那一塊叫:插件管理器。咱們團隊的成員用的語言仍是挺分散的,本人C#,隊長VB.NET,還有其餘成員寫易語言等,系統的功能插件是咱們分開寫的,各自用各自的喜歡的語言寫各個功能模塊的插件,最後用我開發的插件管理器把全部的插件整合到一塊兒。這讓我很頭疼啊,一個C#版的插件,一個VB.NET版的插件,一個易語言的插件,若是有新成員加入,又來個Python版的插件,叫我如何是好。最普通、最爛的處理方法就是:寫不少版本的讀取器,而後使用if來根據插件語言使用對應該版本的讀取器讀取信息,哇,這若是來十幾種語言,豈不是坑爹。可能有人會說:引入Kernel32能解決非.NET語言的插件讀取吧。確實目前使用了這種方案,能兼容易語言,.NET語言就使用.NET的插件Dll讀取方式,可是還不肯定Kernel32能不能解決任何語言的插件。因此爲了讓本身有個後路,我結合題目所說的三種設計模式,寫了一個模板,下面看這個模板能幹嗎,爲何要這樣用。若是有地方使用得不恰當的,但願各位朋友們強拍,我會努力學習改正,若是以爲能夠的,點個推薦,謝謝~python

 

框架展現與說明

                                       

  你們看下類圖,下面分別介紹各個類的職責。Plugin類是插件的數據結構類,PluginType是一個枚舉,它的內容是定義了全部插件的類型,例如DotNet,Python等,擴展的時候須要修改該枚舉。而後剩下的就是本章的重點了,用戶代碼Client使用IPluginAnalyzerable接口來讀取插件信息,該接口有兩個實現類,一個是PluginAnalyzer(抽象類,定義各個語言的讀取器的公共部分),該類實現模板方法模式和責任鏈模式,讓處理命令能在其子類之間互相推卸責任,其子類目前有兩個,分別是DotNet,Python的具體讀取器。最後一個類ComponentAnalyzer擔任封裝職責,將責任鏈初始化好,本身實現單例模式,提供給用戶代碼調用。因此,最後的效果是:獲取ComponentAnalyzer的實例,調用其中一個方法,該方法調用具體讀取器鏈,最後哪一個讀取器處理了請求,用戶是不須要知道的,大概思路就是這樣。編程

 

實現過

  本實現過程只是模板的實現,具體應用到項目中還須要作出相應的改變。設計模式

  事不宜遲,咱們先實現Plugin類和PlugType枚舉:數據結構

    Plugin:架構

class Plugin
    {
        public Plugin(PluginType type,String pluginPath)
        {
            this.BelongType = type;
            this.PluginPath = pluginPath;
        }
        public PluginType BelongType { set; get; }
        public String PluginPath { set; get; }
    }

    PlugType:框架

enum PluginType
    {
        DotNet=0,
        Python=1,
    }

  實現完兩個基本的類型之後,而後就來看下本篇的重頭戲,我會邊貼代碼邊附加上幫助理解的說明。首先從最底層的IPluginAnalyzerable開始:dom

interface IPluginAnalyzerable
    {
         void Analyze(Plugin plugin);
    }

 

  用戶代碼就是使用該接口的Analyze方法來處理傳入的插件的,再下一層有兩個類,一個是抽象類PluginAnalyzer,一個是ComponentAnalyzer。ide

 

abstract class PluginAnalyzer:IPluginAnalyzerable
    {
        public PluginAnalyzer(PluginType type)
        {
            this.analyzerType = type;
        }
        protected PluginType analyzerType;

        public void Analyze(Plugin plugin)
        {
            if (plugin.BelongType == this.analyzerType)
            {
                String author = GetAuthor(plugin);
                String version = GetVersion(plugin);
                Console.WriteLine(String.Format("\r\n分析者:{0},插件類型:{1} \r\n{2}\r\n{3}", 
                    this.GetType().Name,plugin.BelongType,author, version));
            }
            else
            {
                if (nextAnalyzer != null)
                {
                    nextAnalyzer.Analyze(plugin);
                }
            }
        }

        private PluginAnalyzer nextAnalyzer;
        public PluginAnalyzer NextAnalyzer
        {
            set
            {
                this.nextAnalyzer = value;
            }
            get 
            {
                return this.nextAnalyzer;
            }
        }

        protected abstract String GetAuthor(Plugin plugin);
        protected abstract String GetVersion(Plugin plugin);
    }

 

  解讀:函數

  每個繼承本類的具體類都有兩個字段學習

    1:所屬類型(PluginType枚舉),變量名爲analyzerType。  

    2:下一個分析者(PluginAnalyzer),也就是兄弟類(一樣繼承PluginAnalyzer)。

  每個繼承本類的具體類都須要重寫兩個方法GetAuthor和GetVersion,這兩個方法將會在模板方法Analyzer內部被使用。

  模板方法Analyzer首先判斷傳進來的插件類型是否和自身能夠處理的類型相同,若是相同則調用自身的方法處理,若是不一樣則把處理權推給本身的下一位分析者。這樣就完成了具體架構的搭建了。

  

  而後就是具體讀取器類了,各自有各自的處理相同任務的方式。都繼承PluginAnalyzer

    DotNetPluginAnalyzer(該類是處理.NET插件的讀取器)

class DotNetPluginAnalyzer:PluginAnalyzer
    {
        public DotNetPluginAnalyzer(PluginType type) 
            : base(type) 
        { }
        public DotNetPluginAnalyzer()
            : base(PluginType.DotNet)
        { }
        protected override string GetAuthor(Plugin plugin)
        {
            return "DotNet的插件,做者名爲:Jarvin";
        }

        protected override string GetVersion(Plugin plugin)
        {
            return "DotNet的插件,版本號爲:!!!V2014!!";
        }
    }

 

    PythonPluginAnalyzer(該類是處理Python插件的讀取器)

class PythonPluginAnalyzer:PluginAnalyzer
    {
        public PythonPluginAnalyzer(PluginType type) 
            : base(type) 
        { }
        public PythonPluginAnalyzer()
            : base(PluginType.Python)
        { }
        protected override string GetAuthor(Plugin plugin)
        {
            return "Python的插件,做者名爲:Joker";
        }

        protected override string GetVersion(Plugin plugin)
        {
            return "Python的插件,版本號爲:V---很奇怪----";
        }
    }

 

 

  好了,如何使用?我將新建一個類,把這些讀取器包裝起來,而且造成一條鏈,提供一個統一的接口給用戶代碼調用,下面看我如何包裝的。

    ComponentAnalyzeclass ComponentAnalyzer:IPluginAnalyzerable    {

private ComponentAnalyzer() { rootAnalyzer = new DotNetPluginAnalyzer(); PythonPluginAnalyzer pythonAnalyzer = new PythonPluginAnalyzer(); rootAnalyzer.NextAnalyzer = pythonAnalyzer; } #region 單例模式實現
     private ComponentAnalyzer()
     {

     } public static ComponentAnalyzer GetInstance() { return SingleHelper.GetInstance(); } private class SingleHelper { private static ComponentAnalyzer me = new ComponentAnalyzer(); public static ComponentAnalyzer GetInstance() { return me; } } #endregion PluginAnalyzer rootAnalyzer; public void Analyze(Plugin plugin) { rootAnalyzer.Analyze(plugin); } }

  很簡單的一個類,咱們先看構造函數:把全部語言的讀取器鏈接起來,而後鏈頭是rootAnalyzer,咱們每次調用Analyze方法都會調用rootAnalyzer對應的方法,讓其在內部傳遞。弄到這裏,差很少完成了,你們能夠在最下面直接下載源碼運行看結果,下面給出客戶端測試類。

class Programe
    {
        public static void Main(string[] args)
        {
            IPluginAnalyzerable dotnetAnalyzer=ComponentAnalyzer.GetInstance();
            Console.WriteLine("輸入Q退出");
            while(true)
            {
                Plugin plugin = RandomPlugin();
                dotnetAnalyzer.Analyze(plugin);
                if (Console.ReadLine().ToUpper() == "Q")
                {
                    break;
                }
            }
        }
        private static Plugin RandomPlugin()
        {
            Random random = new Random();
            PluginType type = (PluginType)random.Next(0, 2);
            String plaginPath = Path.GetRandomFileName();
            Plugin result = new Plugin(type, plaginPath);
            return result;
        }
    }
View Code

    測試結果:看,以一致的方式執行,可是會獲得不同的效果,責任被推到合適的地方作出相應的處理。

  到這裏有人會說,那如何證實該模型的擴展性?? 好,下面我擴展一種語言讀取器,看我改了多少,對系統影響了多少?

 

擴展性測試

   我以Ruby爲例。

    1.在PluginType中添加一個枚舉內容Ruby:

    2.添加一個Ruby讀取器:

    3.在ComponentAnalyzer(前面說的包裝器)中,把該讀取器添加到鏈條上!

    

    爲了測試,在客戶端代碼中修改Random隨機生成數,使其能生成3,大功告成!(這是測試相關,咱們沒有修改實際客戶端任何代碼)

     順利擴展!!!咱們修改的只是上層的代碼,對於底層,也提供了擴展點,符合對修改封閉,對擴展開放。

 

總結

  完成了,你們也累了,但願有不對的地方你們大力拍,面向組合編程,面向接口編程,不要面向具體實現類編程,這是我學習設計模式感覺最深的一句話。謝謝你們觀看。下面提供完整源碼。

                                   完整Demo下載

相關文章
相關標籤/搜索