MEF——.NET中值得體驗的精妙設計

發布時間: 2011-04-17 18:11  閱讀: 10317 次  推薦: 11   原文鏈接   [收藏]  
摘要:.NET 是 Microsoft XML Web services 平臺。MEF是.NET Framework 4.0一個重要的庫,Visual Studio 2010 Code Editor的擴展支持也是基于MEF構建的。下面讓我們一起來看。

  MEF(Managed Extensibility Framework)是.NET Framework 4.0一個重要的庫,Visual Studio 2010 Code Editor的擴展支持也是基于MEF構建的。MEF的目標是簡化創建可擴展的應用程序,其核心類是ComposablePart,即具有組合能力的組件,每一個稱為ComposablePart(中文可為可組合構件,不過下文一直采用英文來表示,這樣比較貼切)的組件可以組合(稱為Import)其它組件的功能(其它組件通過聲明Export提供功能)并且它也可以通過定義Export將其功能暴露給其它組件。

  ComposablePart通過組件目錄(ComposablePartCatalog)來搜索發現需要的功能,組件目錄可以是一個物理文件目錄、網絡存儲等。每一個ComposablePart還具備動態組合的能力,在必要的情況下可以重新組合功能。本文將采用自底向上的思路體驗一下MEF的設計思想。

  1、無廢話MEF

  MEF的核心是可組合組件ComposablePart,它由ComposablePartDefintion來描述和創建。每一個可組合組件通過定義ExportDefintion向其它組件提供功能,通過ImportDefinition引用其它組件的功能,通過Metadata來描述組件自身的信息。在創建一個ComposablePart組件后,通過在組件目錄(ComposableCatalog)搜索需要的功能實現組件組合。

  2、典型的MEF組合過程

  (1)創建組件目錄(如AssemblyCatalog)

  (2)創建組合容器CompositionContainer,組件容器通過組件目錄搜索組件的定義

  (3)創建一個組件

  (4)從組件容器獲取其它組件功能的定義,然后執行匹配組合

  示例代碼如下:

 
1. var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //創建一個程序集目錄,用于從一個程序集獲取所有的組件定義
2. var container = new CompositionContainer(catalog); //創建一個組合容器
3. var composablePart = new MyComponent();
4. container.ComposeParts(composablePart); //執行組合,從容器中獲取ExportDefinition并創建實例組合在一起
5. // composablePart組合完成以供使用

  其原理如下圖(來自mef.codeplex.com官方網站):

  3 MEF本質組合基元

  組合基元是對提供具有可擴展、可組合能力的組件的本質支持,它處于MEF的最底層,是整個Framework的核心類,由6個類構成,如下圖所示(該圖來自MEF白皮書,白皮書有點抽象,不過看起來很過癮,后面附上本人翻譯的中文版)。

  組合基元類的描述如下:

  (1)ComposablePart:即可組合組件,是組合基元的核心類。ExportDefinitions表示該組件提供的功能的描述;而ImportDefinitions則是對引用其它組件功能的約束的描述。Metadata是對組件自身的特殊標識,當一個ComposablePart通過Import引用其它組件功能時,元數據可能作為滿足引用功能的約束的一個條件。

  (2)ExportDefinition:定義ComposablePart向其它組件提供的功能,這個功能使用一個ContactName和Metadata來描述。ContactName即使用這個功能的契約,Metadata用于進一步描述這個功能。

  (3)ImportDefinition:定義ComposablePart對其它組件提供的功能的引用,即引用了另一個組件的Exports。ImportDefintion使用一個表達式來描述約束,它在Constraint這個屬性定義,其類型為Expression>。這個表達式用于對一個ExportDefintion做匹配判定,其匹配方法如下:

  以下是代碼片段:

 
1. var allExportDefs =// 從ComposablePartCatalog獲取所有ExportDefinition
2. var constraintDelegate= Constraint.Compile(); //編譯成匹配函數的代理
3. var satisfiedExportDefs = allExportDefs .FindAll(constraintDelegate); //使用匹配函數的代理來過濾所有的ExportDefs

  (4)ComposableDefinition:即ComposablePart定義,是ComposablePart的工廠,該類定義了一類ComposablePart引用的功能、暴露的功能及其自身的元數據。引用的功能在ImportDefinitions中描述,暴露的功能通過ExportDefinitions描述。而Metadata則是對組件自身的描述,在MEF中一般用于在一個組件引用(Import)另一個組件功能時,通過對另一個組件的元數據進行匹配,從而來確定是否要組合另一個組件提供的功能。該類是ComposablePart的工廠,提供了CreatePart方法。

  (5)ComposablePartCatalog:可組合組件目錄,用于發現組件,這些組件可能來自物理目錄、網絡存儲等。

  4 、如何使用MEF

  在上面,我們描述了MEF的核心組合基元,組合基元聽起來很簡單,很容易理解,但是想直接使用組合基元來編寫一個ComposablePartDefinition卻不是那么容易了,在MEF的實現,這些類都是一些抽象類,用于描述整個可擴展框架的模型。我先不想說明白MEF到底是如何來使用組合基元,先看示例好了。

  4.1 定義ComposablePartDefinition

  MEF通過引入一個基于特性的編程模型來簡化ComposablePart的定義,如下所示的MessageSender和Processor類均是ComposablePart定義。

  以下是代碼片段:

 
1. public class MessageSender
2. {
3. [Export("MessageSender")]
4. public void Send(string message)
5. {
6. Console.WriteLine(message);
7. }
8. }
9. [Export]
10. public class Processor
11. {
12. [Import("MessageSender")]
13. public Action MessageSender { get; set; }
14. public void Send()
15. {
16. MessageSender("Processed");
17. }
18. }

  4.2、 創建ComposablePart

  以下是代碼片段:

 
1. var catalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //創建一個程序集目錄,用于從一個程序集獲取所有的組件定義
2. var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //創建組件目錄
3. var container = new CompositionContainer(assemblyCatalog); //創建組合容器
4. var processorPart = new Processor();
5. container.ComposeParts(processorPart); //執行組合
6. processorPart.Send();
7. Console.ReadLine();

  4.3 、基于特性編程模型的本質

  通過4.1和4.2的示例可以發現,MessageSender和Processor這兩個類型就是ComposablePartDefintion的實現,在這兩個類型,我們通過Export和Import(ImportMany)特性來定義暴露的功能和引用的功能。  CompositionContainer通過這兩個類所在的程序集的組件目錄來搜索所有的可組合組件定義,然后在執行組合時利用這些定義創建Export對象,根據Import聲明的約束契約實現組件的組合。

  在這個編程模型里面,它允許我們:(1)使用傳統OOP的類型定義來定義一個ComposablePartDefinition,毋庸置疑,這基本沒有引入復雜的概念;(2)使用Export/Import/ImportMany等元數據來聲明組合功能,非常的簡單且容易理解。

  CompositionContainer將會在后臺構建這個Part對應的ComposablePartDefinition以及組件目錄其它ComposablePartDefinition,在執行組合時,利用Definition創建實例執行組合。

  5、 MEF vs MAF vs Unity

  在剛學習MEF時,經常會問一個問題,那就是MEF和MAF這樣的插件框架、和Unity這樣的IoC框架到底有什么區別。MEF與MAF(Managed Addin Framework)最大不同在于:前者關注使用非常簡單的方式來支持具有很強靈活性的可擴展支持,后者關注具有物理隔離、安全、多版本支持的插件平臺架構;MEF和Unity不同在于:前者強調組合,后者強調依賴注入。

  6、 MEF總結

  MEF有3點讓我非常的深刻,首先是組合基元的設計,其次是基于特性的編程模型,最后是MEF的實現方法。

組合基元是可擴展支持的本質,它看起來顯得非常的簡單,但卻有能夠支持強大的功能能力并且不失靈活性。大道至簡,不過,簡的程度確實因人而異,MEF的簡實在讓人佩服得五體投地。這個Framework也是除了ObjectBuilder之外讓我非常喜歡的框架,查看其代碼真是讓人無比舒暢。

  天人之作啊!這幫人的創新能力太強悍了!

  基于特性的編程模型,允許我們使用類的定義 + 特性聲明的方式來定義一個具有組合能力的組件,它使得我們基于MEF編寫組件變得非常非常的簡單!這也讓我再次體會到面向上下文編程方法的魅力~,后面我也會介紹一下我原來做過的一個基于上下文思想設計的FW,和MEF的思路有點類似。

  MEF在實現時,其頂層命名空間是System.ComponentModel.Composition,底下劃分了AttributeModel、Diagnostics、Hosting、Primitives、ReflectionModel命名空間。MEF的頂層命名空間定義了我們使用最多的特性,底下命名空間分別用于定義特性模型、診斷支持、MEF宿主、組合基元、反射模型,整體實現非常的清晰簡潔!看第一眼我就愛上這玩意了!

  7 、基于特性編程模型的另一個示例

  我原來設計了一個基于特性的智能體編程框架。首先,我來簡潔的描述什么是智能體。智能體就是軟件代理人,用軟件來模擬人類的特性,包括智能性、主動性、社會性、感知性等。從實現角度來看,一個智能體就是一個綁定了線程、消息隊列的對象,這個對象用線程來模擬人類大腦,用消息隊列來模擬大腦記憶體。當智能體收到一條消息時,其線程會接管來處理。根據上述描述,大家肯定覺得使用OOP開發智能體有點麻煩。OK,那下面來看看我是如何使用上下文實現智能體的。

  7.1 使用特性來聲明一個具有感知能力和主動性的人

  以下是代碼片段:

 
1. [Agent] 
2. public class SomePerson
3. {
4. [Intelligent]
5. public virtual OpenTheDoor()
6. {
7. // 開門,主動性方法
8. }
9. [Sensible(Environment.Temperature)]
10. public virtual OnTemperatureChanged(SensibilityContext context)
11. {
12. // 當感知到溫度變化的響應,感知性聲明
13. }
14. }

  7.2 創建智能體

  以下是代碼片段: 

 
1. var agentContainer = new AgentContainer();
2. var agent = agentContainer.Build(); //在后臺構建一個真正的智能體
3. agent.OpenTheDoor(); //調用OpenTheDoor方法,這個調用最終會轉變成消息發送給真正的智能體由其本身來執行,就像某人讓另一人去關門一樣,最終將由接收到消息的人去執行關門這個動作。

  AgentFramework具有和MEF類似的設計方法(當然咱們的內功和Microsoft那幫高手沒得比了),通過定義類型 + 聲明智能體特性來定義智能體,這種方式簡單、靈活且可擴展性強!

11
1
 
標簽:MEF
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()