MEF 插件式開發之 小試牛刀


MEF 簡介

Managed Extensibility Framework 即 MEF 是用於創建輕量、可擴展應用程序的庫。 它讓應用程序開發人員得以發現和使用擴展且無需配置。 它還讓擴展開發人員得以輕松地封裝代碼並避免脆弱的緊密依賴性。 MEF 讓擴展不僅可在應用程序內重復使用,還可以跨程序重復使用。

在進行傳統的 C/S 端開發,如果項目不是特別復雜,常規的開發模式還是可以應對的。但是一旦場景復雜度提升,一個小小業務功能的修改就需要更新整個客戶端,這個對於開發者來說是不能忍受的。因此微軟為我們引入了 MEF 的開發模式。允許我們將眾多的業務模塊拆分開來設計成獨立的 DLL,然后由客戶端來進行統一加載,這樣就能解決上述我們所說的痛點。

實踐出真知

創建一個高擴張的 MEF 框架涉及的技術點較多。為了方便初學者能較快理解,上手實踐,我這里主要通過 3 個方面來進行相關敘述。

面向接口編程

如果你還不能理解什么是面向接口編程的話,那你應該還不能區分抽象類和接口之間的區別。其實在剛開始的時候我也不是很能理解,直到我看到了一句話:抽象類規定了你是什么,接口規定了你能干什么,只要你能理解這句話,那么你應該就明白什么是面向接口編程,這種編程方式的好處是統一化了業務的暴露方式,方便外部使用。下面我們看一個簡單的例子。

public interface IMessage
{
    void Send();
}

public class EmailService : IMessage
{
    public void Send()
    {
        Console.WriteLine("Email Send Message");
    }
}

public class SMSService : IMessage
{
    public void Send()
    {
        Console.WriteLine("SMS Send Message");
    }
}

class Program
{
    static void Main(string[] args)
    {
        IMessage email = new EmailService();
        email.Send();

        IMessage sms = new SMSService();
        sms.Send();

        Console.ReadKey();
    }
}

上述代碼中,我們創建了一個 IPlugin 的接口,接口定義了一個 ShowPluginName() 方法,然后我們再定義了兩個獨立的類來分別繼承該接口並實現相應的接口函數。在主函數中,我們只需要定義一個接口類型的對象,然后接收一個具體的類型實例,函數就會輸出對應的正確信息。這樣編程的好處就不言而喻了。代碼很簡單,這里就不過多描述。輸出結果如下圖所示

控制反轉(IOC)

所謂控制反轉,就是將對象初始化的控制權交出去。要實現控制反轉,我們需要有面向接口編程的接口,同樣的,這里也是展示一個代碼段來敘述。

public interface IMessage
{
    void Send();
}

public class EmailService : IMessage
{
    public void Send()
    {
        Console.WriteLine("Email Send Message");
    }
}

public class SMSService : IMessage
{
    public void Send()
    {
        Console.WriteLine("SMS Send Message");
    }
}

public static class Factory
{
    public static EmailService GetEmailService() => new EmailService();

    public static SMSService GetSMSService() => new SMSService();
}

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("DependencyService:");
        DependencyService();

        Console.WriteLine();

        Console.WriteLine("InversionDependencyService:");
        InversionDependencyService();

        Console.ReadKey();
    }

    static void DependencyService()
    {
        EmailService fooEmailService = new EmailService();
        fooEmailService.Send();
    }
    static void InversionDependencyService()
    {
        IMessage fooMessage = Factory.GetEmailService();
        fooMessage.Send();
        fooMessage = Factory.GetSMSService();
        fooMessage.Send();
    }
}

在這個例子中,我們通過工廠模式創建具體的服務,然后供主程序來調用,代碼依然很簡單,分別用傳統創建服務的方式和 控制反轉的方式來進行對比。程序輸出如下

構建入門級 MEF

有了上面兩個知識點做鋪墊,我們可以開始創建一個入門級的 MEF 示例程序。想要在程序中使用 MEF 的話需要引入如下程序集

  • System.ComponentModel.Composition

這里還是以控制台程序來展示。項目結構如下圖所示

  • MefSample.Core:核心接口定義在該項目中
  • MefSample.EmailService:插件,需要引用 MefSample.Core 和 System.ComponentModel.Composition
  • MefSample.SMSService:插件,需要引用 MefSample.Core 和 System.ComponentModel.Composition
  • MefSample:主程序,需要引用 MefSample.Core 和 System.ComponentModel.Composition

注意:上述所有項目程序的輸出目錄需要保持一致

MefSample.Core 代碼段

public interface IMessage
{
    void Send();
}

MefSample.EmailService 代碼段

[Export(typeof(IMessage))]
public class EmailService: IMessage
{
    public void Send()
    {
        Console.WriteLine("Email Send Message");
    }
}

MefSample.SMSService 代碼段

[Export(typeof(IMessage))]
public class SMSService : IMessage
{
    public void Send()
    {
        Console.WriteLine("SMS Send Message");
    }
}

MefSample 代碼段

class Program
{
    static void Main(string[] args)
    {
        var dir = new DirectoryInfo(AppDomain.CurrentDomain.BaseDirectory);
        var catalog = new DirectoryCatalog(dir.FullName, "*.dll");
        using (CompositionContainer container = new CompositionContainer(catalog))
        {
            IEnumerable<IMessage> messages = container.GetExportedValues<IMessage>();
            if (messages != null)
            {
                foreach (var message in messages)
                {
                    message.Send();
                }
            }
        }
        Console.ReadKey();
    }
}

仔細觀察的話,其實上述代碼還是挺簡單的, 我這里使用了 DirectoryCatalog 的方式來尋找目標插件,感興趣的朋友可以試試其他方式:AggregateCatalogAssemblyCatalogDirectoryCatalog。當然,你也可以自定義。程序輸出結果如下圖所示

好了,程序寫到這里相信你對 MEF 也多少有些了解。我沒有過多的講解抽象理論,而是更多地通過代碼來描述我所想要說的。在下篇文章中,我將會簡單講述一下 MEF 在 WPF 中的入門使用,方便初學者更上一層樓。加油,共勉!

相關參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM