概述
這篇文章的目的是簡要分析對比MAF和MEF,并詳細舉出MEF設計中的細節和擴充上的細節,達到讓讀者能實際操作的目的。其中,MAF的設計會附上我的代碼,其實就是官方的代碼我自己手動聯系了一遍,但還是很有收獲的,不動手光看是不會體會到細節的;MEF是我着重介紹的,當然也是微軟推薦的解決方案,是以這部分内容會多一些。
至于為什麼要用MEF(插件架構)讀者可針對自己的項目分析是否有必要使用。
文章中難免有不足和錯誤,還請大家不吝指出,互相交流。
MAF和MEF
MAF是微軟內建在Framework3.5中的為解決插件化程式設計的架構,其優勢是嚴謹但過于死闆,開發速度慢;MEF是內建在Framework4.0中新增加的,潛在的目的是替換MAF的繁瑣而使開發速度增快并且适合絕大多數的工作場景,增強易用性。
下面這篇部落格的作者已經對此分析的很全面了,有興趣的請參考:
http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html
接口契約
無論是MAF和MEF架構均需要一個中間引用集——“契約”,插一句題外話,契約在狹義上來講就是C#中的接口Interface,在廣義上将就是一種限制,幾個“部件”依賴于同一個約定來互相配合、協同,這是人類社會互相協作的精神産物。這種協同思想在軟體領域也是同樣适用的,包括面向服務、面向接口設計、插件化設計、代碼隔離等思想。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuIzNxQjMxUDNxIDN1EDOy8CXxEDNxAjMvwlN2cjNyQzLcd2bsJ2Lc12bj5ycn9Gbi52YuAzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
代碼分析
首先,定義契約層:定義方法和資料接口,僅僅是聲明接口
namespace Practise_MEF.Contract
{
public interface ICalculator
{
String Calculate(String input);
}
public interface IOperation
{
int Operate(int left, int right);
}
public interface IOperationData
{
Char Symbol { get; }
}
public interface IMultiOperation
{
int MultiOperate(Practise_MEF.Core.Module.InputParams p);
}
}
View Code
然後,編寫服務端(Host)解析方法:服務端要定義CompositionContainer對象,此對象需要Add類别對象,包括本程式内部定義的契約實作方法(MyCalculate)和外部PlugIns目錄中的接口方法。
namespace Practise_MEF
{
public class MyCalculateLoader
{
private CompositionContainer _container;
[Import(typeof(ICalculator))]
public ICalculator calculator;
public MyCalculateLoader()
{
//An aggregate catalog that combines multiple catalogs
var catalog = new AggregateCatalog();
//Adds all the parts found in the same assembly as the Program class
catalog.Catalogs.Add(new AssemblyCatalog(typeof(MyCalculate).Assembly));
catalog.Catalogs.Add(new DirectoryCatalog(@"...\Output\PlugIns\"));
//Create the CompositionContainer with the parts in the catalog
_container = new CompositionContainer(catalog);
//Fill the imports of this object
try
{
this._container.ComposeParts(this);
}
catch (CompositionException compositionException)
{
Console.WriteLine(compositionException.ToString());
}
}
}
}
接着,實作MyCalculate契約方法、算法:
namespace Practise_MEF
{
public class MyCalculate
{
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '+')]
public class Add : IOperation
{
public int Operate(int left, int right)
{
return left + right;
}
}
[Export(typeof(IOperation))]
[ExportMetadata("Symbol", '-')]
public class Subtract : IOperation
{
public int Operate(int left, int right)
{
return left - right;
}
}
}
}
在然後,在本程式集中處理插件組方法:稱為插件組是因為可能會有很多插件方法在系統的Container中,需要根據業務需求去區分應用那個插件或者全部;其中operations是系統自動導入的目前加載的插件方法集合。
namespace Practise_MEF
{
[Export(typeof(ICalculator))]
public class MyCalculateAdapter : ICalculator
{
[ImportMany]
IEnumerable<Lazy<IOperation, IOperationData>> operations;
public String Calculate(String input)
{
int left;
int right;
Char operation;
int fn = FindFirstNonDigit(input); //finds the operator
if (fn < 0) return "Could not parse command.";
try
{
//separate out the operands
left = int.Parse(input.Substring(0, fn));
right = int.Parse(input.Substring(fn + 1));
}
catch
{
return "Could not parse command.";
}
operation = input[fn];
foreach (Lazy<IOperation, IOperationData> i in operations)
{
if (i.Metadata.Symbol.Equals(operation))
return i.Value.Operate(left, right).ToString();
}
return "Operation Not Found!";
}
private int FindFirstNonDigit(String s)
{
for (int i = 0; i < s.Length; i++)
{
if (!(Char.IsDigit(s[i]))) return i;
}
return -1;
}
}
}
最後,在編寫擴充的插件方法:
namespace Practise_MEF.Plugin.CalculateEx
{
/// <summary>
/// Mod
/// </summary>
[System.ComponentModel.Composition.Export(typeof(Practise_MEF.Contract.IOperation))]
[System.ComponentModel.Composition.ExportMetadata("Symbol", '%')]
public class CalculateMod : Practise_MEF.Contract.IOperation
{
public int Operate(int left, int right)
{
return left % right;
}
}
}
至此,一個簡單、完整的插件應用已經完成,可實作動态加載處理2個數字的算法(取餘),當軟體需動态增加功能時,隻序編寫xxxxEx.dll,然後Copy到軟體的PlugIns目錄下即可,軟體會動态增加功能。相比而言,MEF的後面隐藏和簡化了更多的操作,是使用者僅僅按需完成幾步操作即可完成軟體的插件化,使項目更靈活。
項目配置
在某個磁盤上建立一個Output檔案夾,并且在Output目錄下建立一個PlugIns檔案夾,名稱要固定,在項目中更改代碼可修改名稱,這不同于MAF約定過于死闆。
- Practise_MEF項目配置
2. Practise_MEF.Contract項目配置
3.Practise_MEF.Core項目配置
4.Practise_MEF.Plugin.CalculateEx項目配置
項目中需要添加項目引用 Practise_MEF.Contract.dll,其屬性設定為 Copy Local 為 False 切記。
同樣輸出的目錄為….\Output\PlugIns\,這樣設定屬性後,Practise_MEF.Contract.dll不會一同輸出到PlugIns目錄中,限制使用Practise_MEF.Plugin.CalculateEx.dll的工程中要保證有Contact.dll。
示例代碼下載下傳
引用
MAF和MEF差別:http://www.cnblogs.com/techborther/archive/2012/02/06/2339877.html
MEF官方解釋:http://msdn.microsoft.com/en-us/library/dd460648.aspx
MEF分析:http://www.360doc.com/content/11/0830/16/7582031_144521508.shtml
作者:Stephen Cui
出處:http://www.cnblogs.com/cuiyansong
版權聲明:文章屬于本人及部落格園共有,凡是沒有标注[轉載]的,請在文章末尾加入我的部落格位址。
如果您覺得文章寫的還不錯,請點選“推薦一下”,謝謝。