三年前,聽當(dāng)時(shí)的師兄推薦,買了蔣波濤的一本關(guān)于GIS插件框架的書。當(dāng)時(shí)一邊看書一邊將其中的例子完整的實(shí)現(xiàn)了一遍,收益匪淺。后來由于項(xiàng)目需要,也做過一個(gè)插件的C/S系統(tǒng),用的是微軟提供的MEF框架。在這個(gè)系統(tǒng)中,把蔣波濤在他的書中沒有涉及到的插件和插件的通信完成了。不過,蔣波濤的那本書,涉及到了插件系統(tǒng)的很多底層內(nèi)容,其中關(guān)于插件引擎的設(shè)計(jì)尤其值得學(xué)習(xí)。近來,我將自己當(dāng)年實(shí)現(xiàn)的那個(gè)例子進(jìn)行了一個(gè)總結(jié),和大家一起分享。
(1).框架分為宿主程序和插件對(duì)象兩部分
(2).兩部分交互基于一種公共的通信契約
(3).宿主程序可以獨(dú)立存在
(1).可以在無需對(duì)程序進(jìn)行重新編譯和發(fā)布的條件下擴(kuò)展程序的功能
(2).可以在不需要程序源代碼的環(huán)境下為程序增加新的功能
(3).在一個(gè)程序的業(yè)務(wù)邏輯不斷發(fā)生改變、新的規(guī)則頻頻加入時(shí)能夠靈活適應(yīng)
(1).基于動(dòng)態(tài)鏈接庫DLL的插件
(2).基于COM的插件
(3).基于反射技術(shù)的插件
接口分為:
宿主接口:IApplicaiton
插件接口:iplugin(ICommand,Itool, IMenuDef,IToolBarDef,IDockableWindowDef)和不是繼承于Ipluging的IItemDef
本章中,對(duì)繼承于Iapplication的Application和繼承于ItemDef的ItemDef類進(jìn)行了實(shí)現(xiàn)。類圖如下:



在實(shí)例化的插件還沒有被添加到宿主中時(shí),需要一個(gè)寄存這些實(shí)例化的插件的宿主。
于是,我們?cè)谠O(shè)計(jì)完插件接口后,還得做一個(gè)設(shè)計(jì)插件容器的工作。此容器只能存放繼承于Iplugin的類。
首先:
繼承CollectionBase抽象類。
因?yàn)镃ollectionBase已經(jīng)實(shí)現(xiàn)了Ilist,Icolloction和Ienumerable等三個(gè)接口,為我們解決了大部分問題。
(其中主要重點(diǎn)是要覆蓋一個(gè)新的GetEnumerator()方法,且此方法返回的是一個(gè)實(shí)現(xiàn)了IEnumerator接口的類.如果在重寫這個(gè)方法時(shí)已經(jīng)利用yield實(shí)現(xiàn)了迭代功能,則第二步可以跳過)。
然后:
寫那個(gè)繼承并實(shí)現(xiàn)了IEnumerator接口的類。(主要是重寫Current,MoveNext,,Reset三個(gè)函數(shù))
以上我們?cè)O(shè)計(jì)了通訊接口和接口容器,為什么說插件只有實(shí)現(xiàn)了這些接口中的一個(gè)才能被識(shí)別呢?
因?yàn)槲覀兊慕涌谝妫≒luginEngine)只能對(duì)這些接口進(jìn)行識(shí)別。
那么何為接口引擎呢?
簡(jiǎn)單點(diǎn)說,在本系統(tǒng)中,就是利用反射后得到的每個(gè)TYPE的InterFace必須是我們以上規(guī)定的幾個(gè)接口才能被識(shí)別和做出反應(yīng)。
反射機(jī)制是我們這個(gè)插件系統(tǒng)的核心技術(shù)。
它使得這些類都可以被動(dòng)態(tài)加載和調(diào)用。
(1).本系統(tǒng)首先利用Directory.GetFiles()函數(shù)得到目標(biāo)文件夾里的所有DLL文件。
(2).利用反射函數(shù)Assemly.LoadFrom加載文件(得到若干程序集)。再利用程序集的GetTypes()得到Type[]數(shù)組。
(3).最后利用Type類的GetInterfaces()得到每一個(gè)類所繼承的接口。利用switch檢查這個(gè)類是否繼承過自定義的那些接口。若實(shí)現(xiàn)過,則利用Activator.CreateInstance(Type _type)這個(gè)方法來實(shí)例化這個(gè)類。最后將其加入到插件容器PluginCollection中。

設(shè)置五個(gè)接口字典容器,分別是裝ICommand,ITool,IToolBarDef,IMenuDef,IDockableWindowDef的五個(gè)容器和一個(gè)命令類型容器(其中將存放實(shí)現(xiàn)了ICommand和或者ITool的不重復(fù)的Category)。
注:這些容器中的Key值都是在實(shí)現(xiàn)這些接口的插件的名字。

利用第三方控件Janus WinForms Controls V3.5來設(shè)計(jì)界面。
此三方控件中有兩個(gè)控件,一個(gè)是UICommandManager,一個(gè)是UIPanelManager,這兩個(gè)控件對(duì)插件的插入顯示有很大的幫助

此宿主實(shí)例化時(shí),首先實(shí)例化一個(gè)Apllication類,然后再給此實(shí)例中的MapControl,PageControl,MainPlantform的重要屬性賦值。
所以通過這個(gè)被復(fù)制了的實(shí)例,宿主和插件的交互就不難實(shí)現(xiàn)了。
這是一個(gè)比較大的方面,也是核心之一了。也便是,怎么樣能讓宿主得到還保留在插件容器中的插件,并能顯示在宿主中?
1.因?yàn)镃ommand和Tool在UI上是同一類型,所以合在一起獲取。
獲取中,有兩個(gè)地方要注意:一個(gè)是要使實(shí)例Create(hook ),即把宿主的相關(guān)信息類Application傳遞過去。第二個(gè)是,注冊(cè)系統(tǒng)定義事件。例如UICommand+=new CommandEventHandler(UICommand_Click);
2.同理分別對(duì)繼承了IMenuDef,IToolBarDef以及IDockableWindowDef的對(duì)象進(jìn)行獲取。
注:浮動(dòng)窗體是由UIPanelManager進(jìn)行托管的。也就是將懸浮框中的ChildHWND(Control類)加入到新panel中的panelContainer.Controls中。
上一個(gè)步驟中,我們把一個(gè)未定義的事件處理方法通過自帶委托注冊(cè)到了Click事件中了。
那么這一節(jié)我們將具體來寫這個(gè)事件處理方法。Command是交互的,Tool是不交互的。所以編寫起來有很大差別。
這兩個(gè)處理函數(shù)中,有兩個(gè)共同的關(guān)鍵點(diǎn):一個(gè)是利用e(CommandEventArgs類)的e.Command.Key得到在相關(guān)插件字典容器中放置的對(duì)象。第二個(gè)是都要觸發(fā)對(duì)象的Click函數(shù)。

這節(jié)是系統(tǒng)以后也可以繼續(xù)擴(kuò)充的地方。
此處重點(diǎn)抓住cAddData這個(gè)插件的實(shí)現(xiàn)。
此類首先繼承Icommand這個(gè)接口。在宿主窗體加載獲取各插件而觸發(fā)的create(Iapplication hook)方法中,調(diào)用Engine自帶的ControlsAddDataCommandClass()類,并將其hook到傳遞過來的宿主的MapControl上去即可。
可以對(duì)宿主程序本身進(jìn)行一些高級(jí)設(shè)計(jì)。比如使得mapControl和PageControl聯(lián)動(dòng)顯示。比如制定TocControl的浮動(dòng)菜單或者開發(fā)要素?cái)?shù)據(jù)的查詢顯示等。
注意:此些設(shè)計(jì)都是對(duì)宿主本身而設(shè)計(jì)的,跟插件沒關(guān)系。是最大程度上的利用宿主窗體本身。
此節(jié)完全是為了更方便的開發(fā)框架或其插件本身而設(shè)計(jì)的。
在此部分,可以把很多以后可能會(huì)用到的方法進(jìn)行編寫以及封裝,以后開發(fā)便可以直接調(diào)用。
可以稱本部分為系統(tǒng)開發(fā)包(SDK),但是此部分并非系統(tǒng)必備。
系統(tǒng)發(fā)布時(shí)首先要確定一個(gè)空機(jī)器上需要運(yùn)行此系統(tǒng)起碼需要哪些基本的平臺(tái)。
此系統(tǒng)運(yùn)行時(shí)對(duì)方機(jī)器上起碼該裝有:.NET 3.0 Framework可再分發(fā)組件包,Janus System UI V3.5 和ArcGIS Engine10 Runtime
然后利用InstallShield ExPRess X來打包。

本框架為純底層開發(fā),移植性和通用性比較好。具有一般的插件所具有的其他特點(diǎn),例如易擴(kuò)展,有一定的解耦性。符合面向接口和依賴倒轉(zhuǎn)的編程思想,在本框架中,還集成了命令模式、觀察者模式、遍歷模式以及外觀模式,單例模式。
本框架劃分的粒度太細(xì),這樣容易使類爆炸式增長(zhǎng)。
本框架只實(shí)現(xiàn)了插件與宿主之間的通信,而沒有實(shí)現(xiàn)插件與插件之間的通信。
解決插件與插件之間的通信,可單獨(dú)做一個(gè)復(fù)雜通信的插件,然后其他插件均引用此插件。利用觀察者模式,在宿主中加載插件后,便能實(shí)現(xiàn)事件注冊(cè),進(jìn)而實(shí)現(xiàn)插件之間的通信。
同時(shí),很多框架都實(shí)現(xiàn)了插件的編程思想。比利用Spring的依賴注入和微軟提供的MEF所有的依賴注入,均能實(shí)現(xiàn)插件系統(tǒng)。
|
新聞熱點(diǎn)
疑難解答
圖片精選
網(wǎng)友關(guān)注