[.NET] Provider Pattern

[.NET] : Provider Pattern

前言 :

在物件導向的程式開發設計上,我們常常強調重用與抽換 :  重用核心的程式碼,抽換特定的模組。
但是大多的物件導向的書,只有介紹重用與抽換的理論,並沒有介紹實做的時候該怎麼寫。
因為實際開發系統,要完成重用與抽換。牽扯到系統設計、動態載入、Config管理......等等方方面的知識。
真的要解釋起來,需要的篇幅會是很大一篇。


本篇的文章跳過這些複雜的理論面,逐步解說如何實做微軟提供的Provider Pattern來實現物件的抽換。
Provider Pattern提供了,使用App.config或是web.config來做儲存裝置,並且實做了物件抽換的功能。
依照本篇的模組去做物件導向開發設計的基礎,可以比較輕鬆架構出類似 [Application Architecture] : 資料存取層 的程式。


IDisplayProvider :

程式的開始先從提供抽換的介面看起。
IDisplayProvider是我們範例裡提供抽換的介面。
AAADisplayProvider 、BBBDisplayProvider是實做抽換界面的物件。
這兩個物件必須是繼承System.Configuration.Provider.ProviderBase及IDisplayProvider。

public interface IDisplayProvider
{
    void Show();
}

public class AAADisplayProvider : ProviderBase, IDisplayProvider
{
    public void Show()
    {
        Console.WriteLine("This is AAA.");
    }
}

public class BBBDisplayProvider : ProviderBase, IDisplayProvider
{
    public void Show()
    {
        Console.WriteLine("This is BBB.");
    }
}

設定檔 :

這邊來看看.config內部的設定

<configuration>

  <configSections>
    <section name="displayProvider" type="ConsoleApplication1.DisplayProviderSection, ConsoleApplication1" />
  </configSections>

  <displayProvider defaultprovider="AAA">
    <providers>
      <add name="AAA" type="ConsoleApplication1.AAADisplayProvider, ConsoleApplication1" />
      <add name="BBB" type="ConsoleApplication1.BBBDisplayProvider, ConsoleApplication1" />
    </providers>
  </displayProvider>

</configuration>

這邊比較不同的,是底下這一段設定檔。
當 defaultProvider設定為AAA的時候,我們在後續的程式碼會取得ConsoleApplication1這個Dll裡面的 ConsoleApplication1.AAADisplayProvider物件來使用。
當 defaultProvider設定為BBB的時候,我們在後續的程式碼會取得ConsoleApplication1這個Dll裡面的 ConsoleApplication1.BBBDisplayProvider物件來使用。

<displayProvider defaultprovider="AAA">
  <providers>
    <add name="AAA" type="ConsoleApplication1.AAADisplayProvider, ConsoleApplication1" />
    <add name="BBB" type="ConsoleApplication1.BBBDisplayProvider, ConsoleApplication1" />
  </providers>
</displayProvider>

關於App.config或是web.config的設定方式可以參考小朱寫的 : [ASP.NET]撰寫自己的 Configuration 區段


使用範例 :

這邊來看看實際使用的範例
依照設定檔的不同,程式建立的物件就會不同。同樣的程式碼在畫面上顯示的資料會不一樣。
當 defaultProvider設定為AAA的時候,畫面顯示 This is AAA.
當 defaultProvider設定為BBB的時候,畫面顯示 This is BBB.

class Program
{
    static void Main(string[] args)
    {
        IDisplayProvider provider = DisplayProviderFactory.Create();
        provider.Show();

        Console.ReadLine();
    }
}

DisplayProviderSection :

DisplayProviderSection主要是定義設定檔的格式。

public class DisplayProviderSection : ConfigurationSection
{
    public const string SectionName = @"displayProvider";

    [ConfigurationProperty("providers", IsDefaultCollection = true)]
    public ProviderSettingsCollection Providers
    {
        get
        {
            return (ProviderSettingsCollection)base["providers"];
        }
    }

    [ConfigurationProperty("defaultProvider")]
    public string DefaultProvider
    {
        get
        {
            return (string)base["defaultProvider"];
        }
        set
        {
            base["defaultProvider"] = value;
        }
    }
}

DisplayProviderFactory :

DisplayProviderFactory是實際讀取設定檔資料,並建立物件的地方。

public class DisplayProviderFactory
{
    public static IDisplayProvider Create()
    {
        return CreateProvider(DisplayProviderSection.SectionName);
    }

    private static IDisplayProvider CreateProvider(string sectionName)
    {
        DisplayProviderSection section = (DisplayProviderSection)ConfigurationManager.GetSection(sectionName);
        if (section == null) throw new ConfigurationErrorsException(string.Format("Can't find Section : {0}.", sectionName));

        IDisplayProvider provider = (IDisplayProvider)InstantiateProvider(section.Providers[section.DefaultProvider], typeof(IDisplayProvider));
        if (provider == null) throw new ConfigurationErrorsException(string.Format("Can't find Provider : {0}.", section.DefaultProvider));

        return provider;
    }

    private static ProviderBase InstantiateProvider(ProviderSettings providerSettings, Type providerType)
    {
        Type settingsType = Type.GetType(providerSettings.Type);
        if (settingsType == null) throw new ConfigurationErrorsException(String.Format("Could not find type: {0}", providerSettings.Type));
        if (!providerType.IsAssignableFrom(settingsType)) throw new ConfigurationErrorsException(String.Format("Provider '{0}' must subclass from '{1}'", providerSettings.Name, providerType));

        ProviderBase provider = Activator.CreateInstance(settingsType) as ProviderBase;
        provider.Initialize(providerSettings.Name, providerSettings.Parameters);
        return provider;
    }
}
期許自己
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。