兩截門(Dutch Door)——(名詞)一個被水平分割爲兩部分的門,這樣每一部分均可以獨立保持開放或者封閉。(《美國傳統英語字典》第4版,2000年)html
假設您設計的程序已經部署到用戶的計算機上,而且可以正常運行了。可是有一天,用戶打來了電話——他們要求增長新的功能。肯定了用戶的需求後,你居然發現原有的軟件架構已經沒法勝任新增任務的需求——你須要從新設計這個應用了!但問題是,就算你又用了一個開發週期完成了用戶須要的應用,卻不能保證用戶的需求不會再次變動。也就是說,需求蔓延的可能性依然存在。所以,這種狀況下插件構架更能顯示出它的優越性。java
一樣的原理,能夠應用到咱們的widows服務開發中。編程
OCP:開放封閉原則api
軟件實體(類、模塊、函數等)應該是能夠擴展的,可是不可修改。微信
這意味着模塊的行爲是能夠擴展的。當應用的需求改變時,咱們能夠對模塊進行擴展,使其具備知足那些改變的新行爲。還句話說,咱們能夠改變模塊的功能。架構
對於模塊行爲進行擴展時,沒必要改動模塊的源代碼或者二進制代碼。模塊的二進制可執行版本,不管是可連接的庫、DLL或者.EXE文件,都無需改動。框架
怎樣可能在不改動模塊源代碼的狀況下去更改它的行爲呢?若是不更改一個模塊,又怎麼可以去改變它的功能呢?tcp
答案是抽象。在C#或者其餘任何的OOPL(面向對象程序設計語言,如java)中,能夠建立出固定卻可以描述一組任意個可能行爲抽象體。這個抽象體就是抽象基類。而這一組任意個可能得行爲則表現爲可能得派生類。函數
模塊可能對抽象體進行操做。因爲模塊依賴於一個固定的抽象體,因此它對於更改能夠是封閉的。同時,經過從這個抽象體派生,能夠擴展此模塊的行爲。網站
插件式架構
插件式架構是遵循OCP原則的。插件式架構,一種開放性的、高擴展性的架構體系。基於插件的設計好處不少,把擴展功能從框架中剝離出來,下降了框架的複雜度,讓框架更容易實現。擴展功能與框架以一種很鬆的方式耦合,二者在保持接口不變的狀況下,能夠獨立變化和發佈。基於插件設計並不神祕,相反它比起一團泥的設計更簡單,更容易理解。
而筆者實際項目中,一般是按照業務歸屬將不一樣業務模塊設計成不一樣插件。
C#實現插件式開發的理論基礎
在dotNET framework中,給開發人員提供了反射機制,經過反射能夠動態加載類庫。
Assembly類能夠得到正在運行的裝配件信息,也能夠動態的加載裝配件,以及在裝配件中查找類型信息,並建立該類型的實例。
Type類能夠得到對象的類型信息,此信息包含對象的全部要素:方法、構造器、屬性等等,經過Type類能夠獲得這些要素的信息,而且調用之。
MethodInfo包含方法的信息,經過這個類能夠獲得方法的名稱、參數、返回值等,而且能夠調用之。
諸如此類,還有FieldInfo、EventInfo等等,這些類都包含在System.Reflection命名空間下。
實現基本目標:
(1)建立一個WinowsService服務,建立三個wcf服務,而後分別實現各自wcf的業務邏輯。以上步驟比較簡單。
(2)而後就是編譯wcf.dll,並拷貝到host服務的指定目錄,好比個人在\plugins\xx_wcf.dll。
(3)在Host宿主的配置文件,添加endpoint和操做契約
<system.serviceModel> <bindings> <netTcpBinding> <binding name="tcpBinding"> <security mode="None"> </security> </binding> </netTcpBinding> </bindings> <services> <!--添加服務--> <!--B服務--> <service name="Plugins.BService.WcfService.BusinessBService" behaviorConfiguration="Plugins.BService.WcfService"> <!--name 必須與代碼中的host實例初始化的服務同樣 behaviorConfiguration 行爲配置 --> <host> <baseAddresses> <!--添加調用服務地址--> <add baseAddress="net.tcp://172.16.0.194:3721/Plugins.BService.WcfService"/> </baseAddresses> </host> <!--添加契約接口 --> <endpoint address="net.tcp://172.16.0.194:3721/Plugins.BService.WcfService" binding="netTcpBinding" contract="Plugins.BService.WcfService.IBusinessBService" bindingConfiguration="tcpBinding" name="Plugins.BService.WcfService.BusinessBasicInfoService"></endpoint> <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/> </service> <!--A服務--> <service name="Plugins.AService.WcfService.BusinessAService" behaviorConfiguration="Plugins.AService.WcfService"> <!--name 必須與代碼中的host實例初始化的服務同樣 behaviorConfiguration 行爲配置 --> <host> <baseAddresses> <!--添加調用服務地址--> <add baseAddress="net.tcp://172.16.0.194:3722/Plugins.AService.WcfService"/> </baseAddresses> </host> <!--添加契約接口 --> <endpoint address="net.tcp://172.16.0.194:3722/Plugins.AService.WcfService" binding="netTcpBinding" contract="Plugins.AService.WcfService.IBusinessAService" bindingConfiguration="tcpBinding" name="Plugins.AService.WcfService.BusinessBasicInfoService"></endpoint> <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/> </service> <!--Resource.Robot服務--> <service name="ResourceRobot.White.WcfService.WhiteListService" behaviorConfiguration="ResourceRobot.White.WcfService"> <!--name 必須與代碼中的host實例初始化的服務同樣 behaviorConfiguration 行爲配置 --> <host> <baseAddresses> <!--添加調用服務地址--> <add baseAddress="net.tcp://172.16.0.194:3723/ResourceRobot.White.WcfService"/> </baseAddresses> </host> <!--添加契約接口 --> <endpoint address="net.tcp://172.16.0.194:3723/ResourceRobot.White.WcfService" binding="netTcpBinding" contract="ResourceRobot.Channels.IWhiteListService" bindingConfiguration="tcpBinding" name="ResourceRobot.White.WcfService.WhiteListService"></endpoint> <endpoint address="mex" binding="mexTcpBinding" name="MEX" contract="IMetadataExchange"/> </service> </services> <!--定義WcfServiceBehavior的行爲--> <behaviors> <serviceBehaviors> <behavior name="Plugins.BService.WcfService"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> <behavior name="Plugins.AService.WcfService"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> <behavior name="ResourceRobot.White.WcfService"> <serviceMetadata httpGetEnabled="false"/> <serviceDebug includeExceptionDetailInFaults="false"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel>
(4)接下來就是利用.NET反射機制,去遍歷plugins目錄下的wcf.dll,分別註冊到ServiceHost中。
在service.cs的OnStart方法中,增長如下代碼:
//1.尋找wcf.dll List<string> pluginpaths = PluginHelper.Find(); //2.遍歷並解析 foreach (string filename in pluginpaths) { try { //獲取文件名 string asmfile = filename; string asmname = Path.GetFileNameWithoutExtension(asmfile); if (asmname != string.Empty) { //利用反射,構造DLL文件的實例 Assembly asm = Assembly.LoadFile(asmfile); //利用反射,從程序集(DLL)中,提取類,並把此類實例化 Type[] t = asm.GetExportedTypes(); foreach (Type type in t) { //3.註冊 ServiceHost host = new ServiceHost(type); if (host != null) { host.Open(); } } } } catch (Exception ex) { Console.Write(ex.Message); }
這樣就實現了一個serivcehost承載多個wcf服務的要求。
3 總結
OCP:開放-封閉原則是敏捷開發中很是重要的原則。
使用插件式架構可以是整個軟件更加靈活,擴展性更強,同時也便於部署和維護。
最後分享一下趣圖:
轉載請註明出處,本文同步更新至