本文将用盡可能簡單的文字來描述插件架構原理。很多人以為插件化很複雜,是以就一直将這類架構阻擋在門外。實際上,在我們的實踐過程中,從架構的使用角度來看,它非常簡單,我們團隊裡面非正規院校畢業的女生也可以來實際使用。如果說插件架構難的地方,我反倒覺得克服人的天然惰性更加困難。我們不能習慣于墨守成規,日複一日年複一年,按照相同的模式來開發,将自己打造成一部“編碼機器”,成為沒有價值的“程式猿/媛”。使用插件架構,沒有多少技術難點,不過需要我們提升我們的軟體開發思想,改變現有開發方式。
1 插件架構本質
在.NET平台,一個程式是由“程式集 + 資源”構成的。程式集是由我們開發的一個個的類。這些類可能是通用功能的輔助類、資料通路類、業務邏輯類,也可以是WinForm應用的窗體類或者Web應用的Web窗體類。以傳統模式開發的程式,一般情況下,不管是我們開發的程式集,還是引用的第三方程式集,它們都在應用程式的bin目錄下,如下所示。

在傳統開發方式中,放在bin目錄下的程式集會由.NET類加載器按需去加載,但是當我們要實作插件化方式開發時,需要依賴于插件架構實作從不同的插件目錄中加載程式集。是以,插件架構本質上是擴充了.NET類加載器的功能,使其能夠從插件目錄中加載程式集。
2 進一步看插件架構
插件化開發方式不僅僅從程式集的組織方式上發上了變化,更重要的是,在功能的組織和實作也發生了變化。我們用一個非常典型的分層架構看看二者差別。
下圖是一個分層架構的應用程式,由表示層、業務層、資料層等組成,每一個層次都有相對應的功能組成,在表示層,我們一般是建構了一個主界面,然後由不同的開發者在主界面上直接放置上菜單及菜單點選事件的響應,同理,其它層次也類似,不同開發者根據需要實作的功能來添加不同的代碼。
在這裡,每一個插件完成一組功能,它們可以有自己獨立的界面、業務邏輯和資料通路實作,插件間具有實體隔離性,開發者可以獨立開發自己的功能,獨立測試、部署與更新,一旦開發完成後,可以由插件架構在進行組合,不再需要進行代碼合并和整體釋出。
3 從簡單示例看插件化
現在我們看看使用插件化來開發的示例。下圖是該軟體的內建後的界面,有5個一級菜單,其中“伺服器”為GPRS通訊伺服器插件定義,由程式猿A開發;“基礎資料、能耗分類、安裝資料”菜單為基礎資料管理插件定義,由程式猿B開發。
該系統使用插件化開發方法如下。
(1)該系統使用了一個通用界面架構插件,你可以從iOpenWorks網站來下載下傳到該插件(http://www.iopenworks.com/Products/ProductDetails/Introduction?proID=386),這個界面架構插件提供了一個空白的界面,這個界面支援三級菜單,允許我們通過以下配置将自己定義的菜單及菜單對應的窗體注冊到主界面。如下XML定義是由GPRS伺服器插件來定義的。它配置了一個一級菜單和兩個二級菜單,以及對應的兩個使用者控件。
<Extension Point="UIShell.WpfShellPlugin.LinkGroups">
<LinkGroup DisplayName="伺服器" DefaultContentSource="UIShell.EcmCommServerPlugin.CommServerUserControl">
<Link DisplayName="通訊伺服器" Source="UIShell.EcmCommServerPlugin.IntegratedCommServerUserControl" />
<Link DisplayName="測試伺服器" Source="UIShell.EcmCommServerPlugin.CommServerUserControl" />
</LinkGroup>
</Extension>
(2)程式猿A建立一個自己的項目來開發通訊伺服器,獨立的實作具體的應用和業務邏輯,其項目結構如下所示。在這裡,他分成兩個項目來實作GPRS通訊伺服器,一個是界面表示層UIShell.EcmCommServerPlugin項目,另一個是業務邏輯層UIShell.EcmCommServerService項目。在開發過程中與程式猿B互相獨立,他們可以獨立開發、測試、部署和維護。
運作這個項目後,程式猿A得到以下的結果。
(3)同理,程式猿B也建立一個自己的項目來開發基礎資料管理,獨立的實作具體的應用和業務邏輯,其項目結構如下所示。在這裡,他分成兩個項目來實作,一個是界面表示層UIShell.EcmConfigurationPlugin項目,另一個是業務邏輯層UIShell.EcmDomainService項目,該項目可以被A來重用。在開發過程中與程式猿A互相獨立,他們可以獨立開發、測試、部署和維護。
運作這個項目後,程式猿B得到以下的結果。
(4)程式猿A和B開發到一個階段的時候,就可以來随時釋出自己的插件,點選項目右鍵,直接将插件釋出到插件倉庫。
以下是釋出的結果,該項目的插件倉庫由3個項目組成。
通訊伺服器項目的插件清單如下。
配置管理項目的插件清單如下所示。
(5)測試人員/部署人員可以通過插件管理來擷取到這兩個程式猿開發的插件,然後将這些插件下載下傳組裝起來。
下載下傳安裝後,就是如下的效果了。
4 插件化有什麼好處
從以上簡單的例子,我們看到,插件化最直接的好處就是可以以子產品化的方式來獨立并行建構軟體系統,在建構的過程中可以随時進行內建。下面我把使用插件化的優點總結一下:
(1) 插件化優化了團隊協作,避免團隊開發過程中互相交叉,不再需要更改各自的代碼将開發的成果內建到一起;
(2) 使用插件化開發後,每一個人的工作都非常的獨立,可以有獨立的架構、獨立開發、獨立測試、獨立部署、獨立更新,并行建構;
(3) 插件化使得重用度更高,在我們開發的一個項目中,超過50%的子產品都直接重用了原有的結果,不需要更改任何的代碼;
(4) 插件化使開發和維護更加簡單,這也是得益于每一個開發人員可以單獨開發自己的應用,每個人都很專注,并且隻需要關注系統中很小的一部分,在以上示例中,每一個開發人員能看到的代碼都是自己來開發的;
(5) 使用插件化開發,我們的釋出、更新也很簡單,右鍵釋出到插件倉庫,部署測試人員通過插件管理界面來下載下傳每一個人的成果,組裝成軟體,并且可以随時更新,這避免了很多手工釋出、內建;
(6) 使用插件化,可以很容易的建構自己的知識庫,是完全可複用,并持續不斷改進和增長。
5 插件架構原理
從上述小節,我們看到,插件化開發更多的是軟體方法和思想上的改變。為了滿足這種開發方法的團隊協作模型,插件架構需要來解決一下幾個問題:
5.1 如何實作子產品化
實作子產品化,意味着我們需要把功能相關的類組織到若幹獨立的程式集,這些程式集是在插件目錄下。是以,實作子產品化就必須解決以下幾個問題:
(1)正确的類加載:即插件架構必須對CLR類加載進行擴充,使其能夠從插件目錄中正确加載到插件程式集;
(2)插件描述:必須引入一個插件描述方法,它來告知目前插件的基本資訊、版本辨別、以及這個插件所包含的程式集;
(3)解決插件的依賴關系:在一個實際應用系統中,必然存在互相依賴。在這裡,依賴是指一個插件使用了另一個插件定義的類型或者建立的對象。也就是說,我們可以直接使用另一個插件定義的類型,在更加麻煩的場景中,還存在循環依賴。插件架構夠必須很好的處理好插件依賴關系的解析與管理。隻有當所有的依賴關系都滿足後,一個插件才能夠對外暴露功能;
(4)類加載空間定義:插件既有自己的程式集,也可以依賴其它插件的程式集,那這時候就必須對插件的類型空間做一個唯一的定義,保證目前插件能夠加載的類型獨立性和隔離性;
(5)插件多版本問題:由于一個插件應用系統中的插件是由若幹團隊開發,每一個團隊獨立開發自己的插件,很有可能一個插件使用了NLog的1.0版本呢,而另一個團隊則更新到最新的1.1版本,這時候,我們必須保證兩個團隊的插件能夠正确運作;
(6)插件狀态的定義:插件可以具備不同的狀态,并在不同狀态下有不同的展現。最常用的有:安裝、解析、正在啟動、激活、正在停止、解除安裝等,當插件處于解析狀态表示這個插件所有依賴關系都已經滿足,可以被正常啟動;當插件處于激活狀态則表示這個插件已經被啟動,所有功能都已經暴露了;
(7)插件啟動初始狀态和啟動順序:當插件被架構加載時,插件處于什麼狀态;用什麼樣的方式來設定插件的啟動順序;當插件有依賴關系時,這時候對啟動順序又有什麼影響;
(8)插件的初始化和終止處理:即相當于插件的入口和出口,當插件被啟動或停止需要執行的操作是如何來定義的。
5.2 如何實作子產品通訊
插件架構必須解決子產品通訊。傳統的通訊,我們都是基于CLR類加載來實作的,即我們可以直接引用另一個程式集的類型,比如:ClassA cls = new ClassA();或者ClassA.Singleton.SayHello()。插件架構可以提供兩種通訊方式,如下:
(1)傳統通訊方式:即我們可以像以前一樣,直接通過引用類型來進行通訊,或可以通過動态加載類型使用反射來通訊;
(2)面向服務:基于SOA模型來實作通訊,即服務 = 服務契約(接口) + 實作(類),服務提供商将服務注冊到總線,服務消費者使用服務契約從服務總線擷取服務,綁定使用。
5.3 如何實作子產品擴充
子產品擴充指的是在不更改已經釋出的插件的任何代碼的情況下,來變更該插件的功能。這也是插件重用的支撐之一。在OSGi.NET插件架構,使用了基于ExtensionPoint-Extension機制,暴露擴充點的插件可以被擴充,擴充的插件通過XML來定義Extension的内容,進而被暴露擴充點的插件來擷取,并變更其功能。
5.4 插件架構進階支援
除了上述的三大基礎功能是插件架構需要實作的,它還可以提供以下更進階的功能:
(1)對自動更新的支援:插件架構可以支援插件的更新,當發現有新版本在插件架構内部時,可以應用更新;此外,還可以對插件架構本身實作自動更新;
(2)對動态性的支援:插件架構可以支援動态安裝、啟動、停止、更新和解除安裝插件,允許靈活控制整個應用系統;
(3)對遠端管理的支援:可以通過遠端管理的API,來實作插件核心的浏覽和管理,進而實作核心情況的檢視和插件的遠端管理控制;
(4)對插件倉庫的支援:可以将插件釋出到插件倉庫,而插件架構則可以利用插件插件倉庫來實作動态安裝和更新,有利于應用系統的知識積累、釋出和團隊協作;
(5)對DevOps的支援:插件開發者可以一鍵釋出更新,而測試人員和部署的應用,則可以及時下載下傳到更新。
關于iOpenWorksSDK下載下傳和疑問,通路:https://www.cnblogs.com/baihmpgy/p/11818026.html。
本文基于Creative Commons Attribution 2.5 China Mainland License釋出,歡迎轉載,演繹或用于商業目的,但是必須保留本文的署名道法自然(包含連結)。如您有任何疑問或者授權方面的協商,請給我留言。