標籤:style class blog c code java
.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:
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開始:
interface IPluginAnalyzerable { void Analyze(Plugin plugin); }
使用者代碼就是使用該介面的Analyze方法來處理傳入的外掛程式的,再下一層有兩個類,一個是抽象類別PluginAnalyzer,一個是ComponentAnalyzer。
abstract class PluginAnalyzer:IPluginAnalyzerable { 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() { base.analyzerType = 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() { this.analyzerType = PluginType.Python; } protected override string GetAuthor(Plugin plugin) { return "Python的外掛程式,作者名為:Joker"; } protected override string GetVersion(Plugin plugin) { return "Python的外掛程式,版本號碼為:V---很奇怪----"; } }
好了,如何使用?我將建立一個類,把這些讀取器封裝起來,並且形成一條鏈,提供一個統一的介面給使用者代碼調用,下面看我如何封裝的。
ComponentAnalyzer
class ComponentAnalyzer:IPluginAnalyzerable { private ComponentAnalyzer() { rootAnalyzer = new DotNetPluginAnalyzer(); PythonPluginAnalyzer pythonAnalyzer = new PythonPluginAnalyzer(); rootAnalyzer.NextAnalyzer = pythonAnalyzer; } #region 單例模式實現 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下載