天天看點

重構筆記---MEF架構(上)

概述

這篇文章的目的是簡要分析對比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,在廣義上将就是一種限制,幾個“部件”依賴于同一個約定來互相配合、協同,這是人類社會互相協作的精神産物。這種協同思想在軟體領域也是同樣适用的,包括面向服務、面向接口設計、插件化設計、代碼隔離等思想。

重構筆記---MEF架構(上)

代碼分析

首先,定義契約層:定義方法和資料接口,僅僅是聲明接口

重構筆記---MEF架構(上)
重構筆記---MEF架構(上)
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目錄中的接口方法。

重構筆記---MEF架構(上)
重構筆記---MEF架構(上)
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契約方法、算法:

重構筆記---MEF架構(上)
重構筆記---MEF架構(上)
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是系統自動導入的目前加載的插件方法集合。

重構筆記---MEF架構(上)
重構筆記---MEF架構(上)
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約定過于死闆。

  1. Practise_MEF項目配置
重構筆記---MEF架構(上)

  2. Practise_MEF.Contract項目配置

重構筆記---MEF架構(上)

  3.Practise_MEF.Core項目配置

重構筆記---MEF架構(上)

  4.Practise_MEF.Plugin.CalculateEx項目配置

重構筆記---MEF架構(上)
重構筆記---MEF架構(上)

項目中需要添加項目引用 Practise_MEF.Contract.dll,其屬性設定為 Copy Local 為 False 切記。

重構筆記---MEF架構(上)

同樣輸出的目錄為….\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

版權聲明:文章屬于本人及部落格園共有,凡是沒有标注[轉載]的,請在文章末尾加入我的部落格位址。

如果您覺得文章寫的還不錯,請點選“推薦一下”,謝謝。

繼續閱讀