文章版權由做者李曉暉和博客園共有,若轉載請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/。編程
研一時,聽當時的師兄推薦,買了蔣波濤的一本關於GIS插件框架的書。當時一邊看書一邊將其中的例子完整的實現了一遍,收益匪淺。後來因爲項目須要,也作過一個插件的C/S系統,用的是微軟提供的MEF框架。在這個系統中,把蔣波濤在他的書中沒有涉及到的插件和插件的通訊完成了。不過,蔣波濤的那本書,涉及到了插件系統的不少底層內容,其中關於插件引擎的設計尤爲值得學習。近來,我將本身當年實現的那個例子進行了一個總結,和你們一塊兒分享。數組
(1).框架分爲宿主程序和插件對象兩部分微信
(2).兩部分交互基於一種公共的通訊契約app
(3).宿主程序能夠獨立存在框架
(1).能夠在無需對程序進行從新編譯和發佈的條件下擴展程序的功能函數
(2).能夠在不須要程序源代碼的環境下爲程序增長新的功能學習
(3).在一個程序的業務邏輯不斷髮生改變、新的規則頻頻加入時可以靈活適應插件
(1).基於動態連接庫DLL的插件設計
(2).基於COM的插件orm
(3).基於反射技術的插件
接口分爲:
宿主接口:IApplicaiton
插件接口:IPlugin(ICommand,Itool, IMenuDef,IToolBarDef,IDockableWindowDef)和不是繼承於Ipluging的IItemDef
本章中,對繼承於IApplication的Application和繼承於ItemDef的ItemDef類進行了實現。類圖以下:
在實例化的插件尚未被添加到宿主中時,須要一個寄存這些實例化的插件的宿主。
因而,咱們在設計完插件接口後,還得作一個設計插件容器的工做。此容器只能存放繼承於Iplugin的類。
首先:
繼承CollectionBase抽象類。
由於CollectionBase已經實現了Ilist,Icolloction和Ienumerable等三個接口,爲咱們解決了大部分問題。
(其中主要重點是要覆蓋一個新的GetEnumerator()方法,且此方法返回的是一個實現了IEnumerator接口的類.若是在重寫這個方法時已經利用yield實現了迭代功能,則第二步能夠跳過)。
而後:
寫那個繼承並實現了IEnumerator接口的類。(主要是重寫Current,MoveNext,,Reset三個函數)
以上咱們設計了通信接口和接口容器,爲何說插件只有實現了這些接口中的一個才能被識別呢?
由於咱們的接口引擎(PluginEngine)只能對這些接口進行識別。
那麼何爲接口引擎呢?
簡單點說,在本系統中,就是利用反射後獲得的每一個TYPE的InterFace必須是咱們以上規定的幾個接口才能被識別和作出反應。
反射機制是咱們這個插件系統的核心技術。
它使得這些類均可以被動態加載和調用。
(1).本系統首先利用Directory.GetFiles()函數獲得目標文件夾裏的全部DLL文件。
(2).利用反射函數Assemly.LoadFrom加載文件(獲得若干程序集)。再利用程序集的GetTypes()獲得Type[]數組。
(3).最後利用Type類的GetInterfaces()獲得每個類所繼承的接口。利用switch檢查這個類是否繼承過自定義的那些接口。若實現過,則利用Activator.CreateInstance(Type _type)這個方法來實例化這個類。最後將其加入到插件容器PluginCollection中。
設置五個接口字典容器,分別是裝ICommand,ITool,IToolBarDef,IMenuDef,IDockableWindowDef的五個容器和一個命令類型容器(其中將存放實現了ICommand和或者ITool的不重複的Category)。
注:這些容器中的Key值都是在實現這些接口的插件的名字。
利用第三方控件Janus WinForms Controls V3.5來設計界面。
此三方控件中有兩個控件,一個是UICommandManager,一個是UIPanelManager,這兩個控件對插件的插入顯示有很大的幫助
此宿主實例化時,首先實例化一個Apllication類,而後再給此實例中的MapControl,PageControl,MainPlantform的重要屬性賦值。
因此經過這個被複制了的實例,宿主和插件的交互就不難實現了。
這是一個比較大的方面,也是核心之一了。也即是,怎麼樣能讓宿主獲得還保留在插件容器中的插件,並能顯示在宿主中?
1.由於Command和Tool在UI上是同一類型,因此合在一塊兒獲取。
獲取中,有兩個地方要注意:一個是要使實例Create(hook ),即把宿主的相關信息類Application傳遞過去。第二個是,註冊系統定義事件。例如UICommand+=new CommandEventHandler(UICommand_Click);
2.同理分別對繼承了IMenuDef,IToolBarDef以及IDockableWindowDef的對象進行獲取。
注:浮動窗體是由UIPanelManager進行託管的。也就是將懸浮框中的ChildHWND(Control類)加入到新panel中的panelContainer.Controls中。
上一個步驟中,咱們把一個未定義的事件處理方法經過自帶委託註冊到了Click事件中了。
那麼這一節咱們將具體來寫這個事件處理方法。Command是交互的,Tool是不交互的。因此編寫起來有很大差異。
這兩個處理函數中,有兩個共同的關鍵點:一個是利用e(CommandEventArgs類)的e.Command.Key獲得在相關插件字典容器中放置的對象。第二個是都要觸發對象的Click函數。
這節是系統之後也能夠繼續擴充的地方。
此處重點抓住cAddData這個插件的實現。
此類首先繼承Icommand這個接口。在宿主窗體加載獲取各插件而觸發的create(Iapplication hook)方法中,調用Engine自帶的ControlsAddDataCommandClass()類,並將其hook到傳遞過來的宿主的MapControl上去便可。
能夠對宿主程序自己進行一些高級設計。好比使得mapControl和PageControl聯動顯示。好比制定TocControl的浮動菜單或者開發要素數據的查詢顯示等。
注意:此些設計都是對宿主自己而設計的,跟插件不要緊。是最大程度上的利用宿主窗體自己。
此節徹底是爲了更方便的開發框架或其插件自己而設計的。
在此部分,能夠把不少之後可能會用到的方法進行編寫以及封裝,之後開發即可以直接調用。
能夠稱本部分爲系統開發包(SDK),可是此部分並不是系統必備。
系統發佈時首先要肯定一個空機器上須要運行此係統起碼須要哪些基本的平臺。
此係統運行時對方機器上起碼該裝有:.NET 3.0 Framework可再分發組件包,Janus System UI V3.5 和ArcGIS Engine10 Runtime
而後利用InstallShield Express X來打包。
本框架爲純底層開發,移植性和通用性比較好。具備通常的插件所具備的其餘特色,例如易擴展,有必定的解耦性。符合面向接口和依賴倒轉的編程思想,在本框架中,還集成了命令模式、觀察者模式、遍歷模式以及外觀模式,單例模式。
本框架劃分的粒度太細,這樣容易使類爆炸式增加。
本框架只實現了插件與宿主之間的通訊,而沒有實現插件與插件之間的通訊。
解決插件與插件之間的通訊,可單獨作一個複雜通訊的插件,而後其餘插件均引用此插件。利用觀察者模式,在宿主中加載插件後,便能實現事件註冊,進而實現插件之間的通訊。
同時,不少框架都實現了插件的編程思想。比利用Spring的依賴注入和微軟提供的MEF全部的依賴注入,均能實現插件系統。
-----歡迎轉載,但保留版權,請於明顯處標明出處:http://www.cnblogs.com/naaoveGIS/
若是您以爲本文確實幫助了您,能夠微信掃一掃,進行小額的打賞和鼓勵,謝謝 ^_^