哇,看到題目挺長的,這個組合型的東西,到底能幹啥呢?本篇文章來一塊兒琢磨琢磨,這兩天爲了團隊的軟件趕工,我負責的那一塊叫:插件管理器。咱們團隊的成員用的語言仍是挺分散的,本人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; } }
測試結果:看,以一致的方式執行,可是會獲得不同的效果,責任被推到合適的地方作出相應的處理。
到這裏有人會說,那如何證實該模型的擴展性?? 好,下面我擴展一種語言讀取器,看我改了多少,對系統影響了多少?
擴展性測試
我以Ruby爲例。
1.在PluginType中添加一個枚舉內容Ruby:。
2.添加一個Ruby讀取器:
3.在ComponentAnalyzer(前面說的包裝器)中,把該讀取器添加到鏈條上!
爲了測試,在客戶端代碼中修改Random隨機生成數,使其能生成3,大功告成!(這是測試相關,咱們沒有修改實際客戶端任何代碼)
順利擴展!!!咱們修改的只是上層的代碼,對於底層,也提供了擴展點,符合對修改封閉,對擴展開放。
總結
完成了,你們也累了,但願有不對的地方你們大力拍,面向組合編程,面向接口編程,不要面向具體實現類編程,這是我學習設計模式感覺最深的一句話。謝謝你們觀看。下面提供完整源碼。