徐龍
摘 要:利用插件技術(shù),人們可以隨時擴展宿主程序的功能,這不僅提高了程序的擴展性,也降低了宿主程序域各插件功能上的耦合,使程序變得更加容易維護。
關(guān)鍵詞:NET Framework;MEF;插件;解耦
1 MEF介紹
Managed Extensibility Framework(MEF)是.NET平臺下的一個擴展性管理框架,它是一系列特性的集合,包括依賴注入等。MEF為開發(fā)人員提供了一個工具,讓我們可以輕松地對應(yīng)用程序進行擴展并且對已有的代碼產(chǎn)生最小的影響。在開發(fā)過程中,研發(fā)人員可以根據(jù)業(yè)務(wù)需求定義一些擴展點,在后期的維護中便可使用這些擴展點與應(yīng)用程序交互;同時MEF使得宿主程序與擴展程序之間不產(chǎn)生直接的依賴,這樣做的好處之一是多個具有同樣的擴展需求之間可以很容易共享擴展程序。
1.1 MEF體系結(jié)構(gòu)
MEF通過部件和容器來構(gòu)建。容器在類別中查找部件,類別在程序集或目錄中查找部件。容器把入口連接到出口上,部件因此可以用于宿主應(yīng)用程序。
MEF的核心技術(shù)是組合部件,它由一系列特性來描述和創(chuàng)建。每一個部件通過附加Export特性向其他部件提供功能,并通過附加Import特性來引用其他部件的功能。創(chuàng)建一個部件后,通過在定義好的部件目錄中查找需要的功能來實現(xiàn)部件的組裝。
MEF包括3大類別:用于宿主的類、基元類和基于特性機制的類。宿主類包含類別和容器。基元類可以用作基類,來擴展MEF體系結(jié)構(gòu),以使用其他技術(shù)連接出口和入口。通過反射構(gòu)成基于特性機制的實現(xiàn)方式的類,如Export和Import特性等,提供擴展方法,便于使用的基于特性的部件的類,這些也是MEF的一部分。
1.2 MEF部件的構(gòu)建過程
通過以下步驟便可輕松構(gòu)建MEF程序:
(1)定義部件目錄
(2)創(chuàng)建部件容器
(3)創(chuàng)建部件
(4)部件組裝與實例化
1.3 聲明一個部件
導(dǎo)出是部件向容器中的其他部件提供的一個值,而“導(dǎo)入”是部件向要通過可用導(dǎo)出滿足的容器提出的要求。在特性化編程模型中,導(dǎo)入和導(dǎo)出是由修飾類或成員使用Import和Export特性聲明的。Export特性可修飾類、字段、屬性或方法,而Import特性可修飾字段、屬性或構(gòu)造函數(shù)參數(shù)。為了使導(dǎo)入與導(dǎo)出匹配,導(dǎo)入和導(dǎo)出必須具有相同的協(xié)定。
1.4 部件的生命周期
由于部件承載于組合容器中,因此其生命周期可能比普通對象更復(fù)雜。需要在關(guān)閉時執(zhí)行工作的部件和需要釋放資源的部件應(yīng)照常為NET Framework對象實現(xiàn)IDisposable。但是,由于容器創(chuàng)建并維護對部件的引用,因此只有擁有部件的容器才應(yīng)對其調(diào)用Dispose方法。容器本身實現(xiàn)IDisposable,并且作為Dispose中其清理的一部分,它將對擁有的所有部件調(diào)用Dispose。因此,當不再需要組合容器及其擁有的任何部件時,您應(yīng)始終釋放該組合容器。
2 利用MEF技術(shù)解耦程序
下文通過開發(fā)簡單的數(shù)據(jù)庫入庫工具來逐步說明。
2.1 簡單的入庫工具
假設(shè)需要開發(fā)一款數(shù)據(jù)庫入庫工具,并且準備采用SQL Server作為數(shù)據(jù)庫管理系統(tǒng),可以使用在.NET下ADO.NET 進行數(shù)據(jù)庫訪問。
如果使用一段時間后,需要兼容其他的數(shù)據(jù)庫,如Oracle、MySql等,這就可能需要修改代碼,以適應(yīng)新的需求。
很顯然,訪問數(shù)據(jù)庫的業(yè)務(wù)邏輯與主程序產(chǎn)生了緊耦合,使得程序在需求改變后很難擴展,這不符合OCP(Open-Closed Principle)原則。
2.2 利用MEF技術(shù)實現(xiàn)解耦
上述問題如果使用MEF技術(shù)則很容易解決。將入庫工具看作宿主程序,將入庫的具體業(yè)務(wù)放入插件中執(zhí)行。通過添加插件來實現(xiàn)兼容不同的數(shù)據(jù)庫。
2.2.1 定義Import類型
在宿主程序中添加類型為IDataAccess的屬性,并將Import特性附加其上。這樣在程序運行中,部件在目錄中查找接口協(xié)定,并實例化。而部件的組裝是通過反射技術(shù)動態(tài)進行的,這樣便可在任何時候在滿足接口協(xié)定的情況下根據(jù)需要來實現(xiàn)IDataAccess。
應(yīng)當將IDataAccess放在獨立的程序集中,這樣宿主程序集和插件程序集可以分別引用IDataAccess所在的程序集,從而避免了宿主程序集直接應(yīng)用插件程序集而產(chǎn)生不必要的耦合。
2.2.2 定義Ecport
在實現(xiàn)IDataAccess的類中,需要加入Export特性,這是插件實現(xiàn)個關(guān)鍵步驟。因為部件在組裝過程中根據(jù)Export特性來匹配和實例化。
MEF時基于反射來實現(xiàn)的。這樣在后期需要兼容其他數(shù)據(jù)庫時,只要新建獨立的程序集并使用需要的數(shù)據(jù)庫訪問類實現(xiàn)IDataAccess接口,將上述程序集拷貝到宿主程序的運行目錄中即可。
2.2.3 部件組裝
部件組裝很容易實現(xiàn),只需定義部件目錄DirectoryCatalog,并使用上述對象實例化部件容器CompositionContainer,同時使用部件容器的ComposeParts()方法,從特性化對象中創(chuàng)建可組合部件,并在當前容器中組合這些部件。只需3行代碼便可完成部件的組裝,具體如下:
var catalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
var container = new CompositionContainer(catalog);
container.ComposeParts(this);
3 總結(jié)
MEF的組合基元是可擴展支持的本質(zhì),它看起來顯得非常的簡單,但卻有能夠支持強大的功能并且不失靈活性。上例中正是利用這種原理輕松的實現(xiàn)了主程序與具體的業(yè)務(wù)之間的解耦。
參考文獻
[1]ChristianNagel ProfessionalC#4and. NET4[M].清華大學(xué)出版社,2010.
(作者單位:安徽四創(chuàng)電子股份有限公司)