天天看點

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() 方法,然後我們再定義了兩個獨立的類來分别繼承該接口并實作相應的接口函數。在主函數中,我們隻需要定義一個接口類型的對象,然後接收一個具體的類型執行個體,函數就會輸出對應的正确資訊。這樣程式設計的好處就不言而喻了。代碼很簡單,這裡就不過多描述。輸出結果如下圖所示

MEF 插件式開發 - 小試牛刀

所謂控制反轉,就是将對象初始化的控制權交出去。要實作控制反轉,我們需要有面向接口程式設計的接口,同樣的,這裡也是展示一個代碼段來叙述。

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

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

MEF 插件式開發 - 小試牛刀
  • 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 的方式來尋找目标插件,感興趣的朋友可以試試其他方式:

AggregateCatalog

AssemblyCatalog

DirectoryCatalog

。當然,你也可以自定義。程式輸出結果如下圖所示

MEF 插件式開發 - 小試牛刀

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