打造属于你的提供者(Provider = Strategy + Factory Method) 设计模式 - Provider Pattern(提供者模式)...

本文介绍了提供者模式(Provider),一种结合了策略模式和工厂方法的设计思想。通过.NET Framework中的ProviderBase类,展示了如何实现动态调用,并通过自定义数据库提供者的例子,解释了提供者模式如何在不同数据库间无缝切换。文中还探讨了提供者模式与继承耦合度的关系,并提供了配置文件设置和测试案例。
摘要由CSDN通过智能技术生成

打造属于你的提供者(Provider = Strategy + Factory Method)

 

1.1.1 摘要

      在日常系统设计中,我们也许听说过提供者模式,甚至几乎每天都在使用它,在.NET Framkework 2.0中微软提出了提供者模式(Provider),所以我们可以发现.NET Framkework中有很多类命名都含有“Provider”,例如:IFormatProvider,RSACryptoServiceProvider等等,由此可见它们都间接或直接使用了提供者模式这一设计思想,现在让我们来介绍一下提供者模式(Provider)。

 

1.1.2 正文

      首先让我们通过提供者模式(Provider)结构图,了解什么是提供者模式(Provider)。

 

provdier0

图1提供者模式(Provider)结构图

 

       通过上面的结构图我们发现提供者模式(Provider),并没有想象中的那么复杂而且整个结构就是使用了一些继承关系而已。

       提供者模式(Provider)的结构图,和我们熟悉的策略模式(Strategy)结构基本一致就是通过继承扩展不同种类的算法。

       策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。

 

http://www.cnblogs.com/rush/

图2策略模式(Strategy)结构图

 

       所以很多人都喜欢把提供者模式(Provider)和策略模式(Strategy)进行对比,更甚至有些人认为就是策略模式(Strategy),其实不然我们可以发现每种提供者它们都是继承于一个基类ProviderBase,无论是系统还是自定义的提供者都必须继承于它。

现在让我们看看抽象类ProviderBase。

 

public abstract class ProviderBase
{
    // Fields
    private string _Description;
    private bool _Initialized;
    private string _name;

    // Methods
    protected ProviderBase();
    public virtual void Initialize(string name, NameValueCollection config);

    // Properties
    public virtual string Description { get; }
    public virtual string Name { get; }
}

 

       通过上面的代码我们发现,ProviderBase只包含一些描述字段和属性,还有就是一个无参构造函数和一个初始化方法Initialize(string name, NameValueCollection config)。该方法是提供者模式实现动态调用的核心方法(DI的实现)。

       在ProviderBase中提供了Initialize方法实现和统一签名,然后让提供者重写该方法,现在我们又有问题了究竟这个方法要实现什么功能呢?让我们看看Initialize方法的实现吧!

 

public virtual void Initialize(string name, NameValueCollection config)
{
    lock (this)
    {
        if (this._Initialized)
        {
            throw new InvalidOperationException(
                SR.GetString("Provider_Already_Initialized"));
        }
        this._Initialized = true;
    }
    if (name == null)
    {
        throw new ArgumentNullException("name");
    }
    if (name.Length == 0)
    {
        throw new ArgumentException(
            SR.GetString("Config_provider_name_null_or_empty"), "name");
    }
    this._name = name;
    if (config != null)
    {
        this._Description = config["description"];
        config.Remove("description");
    }
}

 

       其实这个方法功能很简单就是去读我们配置文件相应节点的值, 现在我们知道可以通过重新 Initialize 方法,可以实现从配置文件中读取 Provider 的信息(如:name,type,connectionString 等)。我们都知道继承使得类之间的耦合度增加, 这就是策略模式的一个缺点具体算法通过继承来进行扩展。但我们看到提供者模式(Provider)使用了一种比较灵活方法对具体提供者进行扩展。

 

provider1

图 3 ASP.NET中的提供者模式(Provider)

 

       通过上图我们发现.NET中的MembershipProvider,RoleProvider,SiteMapProvider等提供者都必须继承于ProviderBase类,接着是具体实现提供者的类。除此之外,我们自定义提供者都必须继承于ProviderBase类。

       我们对于提供者模式(Provider)有了初步的认识,那么现在让我们定义属于自己的提供者吧!

       假设我们要设计数据库提供者,考虑到我们系统的灵活性和扩展性,我们应该使系统可以在不同数据之间无缝切换,由于数据库的种类有:SqlServer,Oracle,MySql,SQLite等,首先定义一个抽象类DataProvider让具体数据提供者来实现它。

 

provider2

图 4 DataProvider的设计

 

       我们定义一个DataProvider类继承于ProviderBase,然后添加数据连接字符串,存储过程名字和参数属性,最后就是一系列的对数据库操作方法。

 

/// <summary>
/// Defines the methods that Data providers should be implemented.
/// </summary>
public abstract class DataProvider : ProviderBase
{


    /// <summary>
    /// Runs the specified transaction.
    /// </summary>
    /// <param name="transaction">The transaction.</param>
    public abstract void Run(DbTransaction transaction);


    /// <summary>
    /// Runs the specified transaction.
    /// </summary>
    /// <param name="transaction">The transaction.</param>
    /// <param name="parameters">The stored procedure  parameters.</param>
    public abstract void Run(DbTransaction transaction, DbParameter[] parameters);


    //public abstract DbDataReader Run(DbParameter[] parameters);


    /// <summary>
    /// Runs the specified connection string.
    /// </summary>
    /// <param name="connectionString">The connection string.</param>
    /// <param name="parameters">The stored procedure  parameters.</param>
    /// <returns>Returns dataset.</returns>
    public abstract DataSet Run(string connectionString, DbParameter[] parameters);


    /// <summary>
    /// Runs the scalar.
    /// </summary>
    /// <param name="connectionString">The connection string.</param>
    /// <param name="parameters">The stored procedure  parameters.</param>
    /// <returns>Returns an object.</returns>
    public abstract object RunScalar(string connectionString, DbParameter[] parameters);


    /// <summary>
    /// Runs the scalar.
    /// </summary>
    /// <param name="transaction">The transaction.</param>
    /// <param name="parameters">The stored procedure  parameters.</param>
    /// <returns></returns>
    public abstract object RunScalar(SqlTransaction transaction, DbParameter[] parameters);


    /// <summary>
    /// Runs the specified connectionstring.
    /// </summary>
    /// <param name="connectionstring">The connectionstring.</param>
    /// <returns></returns>
    public abstract DataSet Run(string connectionstring);


    /// <summary>
    /// Runs this instance.
    /// </summary>
    public abstract void Run();


    /// <summary>
    /// Runs the specified parameters.
    /// </summary>
    /// <param name="parameters">The stored procedure  parameters.</param>
    /// <returns></returns>
    public abstract DataSet Run(DbParameter[] parameters);


    /// <summary>
    /// Runs the specified command type.
    /// </summary>
    /// <param name="commandType">Type of the command.</param>
    /// <param name="commandText">The command text stored procedure or sql queries.</param>
    /// <returns></returns>
    public abstract DbDataReader Run(CommandType commandType, string commandText);


    /// <summary>
    /// Gets or sets the stored procedure parameters.
    /// </summary>
    /// <value>
    /// The stored procedure parameters.
    /// </value>
    public DbParameter[] Parameters { get; set; }


    /// <summary>
    /// Gets or sets the name of the stored procedure.
    /// </summary>
    /// <value>
    /// The name of the stored procedure.
    /// </value>
    public string StoredProcedureName { get; set; }


    /// <summary>
    /// Gets the default connection string from webconfig file.
    /// </summary>
    protected string ConnectionString
    {
        get { return ConfigurationManager.ConnectionStrings["SQLCONN"].ToString(); }
    }
}

 

       上面我们完成了抽象的提供者,这里仅仅是一些抽象的方法并没有具体的实现,所以我们要通过具体的提供者来实现这些抽象的方法。接下来让我们定义具体的提供者SqlDataProvider和OracleDataProvider。

 

provider3

图 5具体提供者设计

 

/// <summary>
/// The implementor of DataProvder.
/// </summary>
public class SqlDataProvider : DataProvider
{

    #region Fields

    private string _connection = string.Empty;

    #endregion

    /// <summary>
    /// Default parameterless constructor used by Reflection.
    /// </summary>
    public SqlDataProvider()
    {
        // Just used by Reflection.
    }

    /// <summary>
    /// Initializes the provider.
    /// </summary>
    /// <param name="name">The name of provider, setting in webconfig file.</param>
    /// <param name="config">The value collection of config.</param>
    public override void Initialize(string name, NameValueCollection config)
    {
        // Due to ProviderBase has check name and config are available or not,
        // So no need validate name and config.
        base.Initialize(name, config);
        _connection = config["connectionString"];

        if (string.IsNullOrEmpty(_connection))
            throw new ConfigurationErrorsException("Connection string can't empty.");
    }


    /// <summary>
    /// Runs the specified transaction.
    /// </summary>
    /// <param name="transaction">The transaction.</param>
    public override void Run(DbTransaction transaction)
    {
        SqlHelper.ExecuteNonQuery(transaction as SqlTransaction,
            CommandType.StoredProcedure, StoredProcedureName, Parameters);
    }


    /// <summary>
    /// Runs the specified transaction.
    /// </summary>
    /// <param name="transaction">The transaction.</param>
    /// <param name="parameters">The parameters.</param>
    public override void Run(DbTransaction transaction, DbParameter[] parameters)
    {
        SqlHelper.ExecuteNonQuery(transaction as SqlTransaction,
            CommandType.StoredProcedure, StoredProcedureName, parameters as SqlParameter[]);
    }


    /// <summary>
    /// Runs the specified connection string.
    /// </summary>
    /// <param name="connectionString">The connection string.</param>
    /// <param name="parameters">The parameters.</param>
    /// <returns></returns>
    public override DataSet Run(string connectionString, DbParameter[] parameters)
    {
        DataSet ds = SqlHelper.ExecuteDataset(connectionString, StoredProcedureName, parameters);
        return ds;
    }


    /// <summary>
    /// Runs the specified parameters.
    /// </summary>
    /// <param name="parameters">The stored procedure  parameters.</param>
    /// <returns></returns>
    public override DataSet Run(DbParameter[] parameters)
    {
        DataSet ds = SqlHelper.ExecuteDataset(ConnectionString, StoredProcedureName, parameters);

        return ds;

    }


    /// <summary>
    /// Runs the scalar.
    /// </summary>
    /// <param name="connectionString">The connection string.</param>
    /// <param name="parameters">The parameters.</param>
    /// <returns></returns>
    public override object RunScalar(string connectionString, DbParameter[] parameters)
    {
        object obj = SqlHelper.ExecuteScalar(connectionString, StoredProcedureName, parameters);

        return obj;
    }


    /// <summary>
    /// Runs the scalar.
    /// </summary>
    /// <param name="transaction">The transaction.</param>
    /// <param name="parameters">The parameters.</param>
    /// <returns></returns>
    public override object RunScalar(SqlTransaction transaction, DbParameter[] parameters)
    {
        object obj = SqlHelper.ExecuteScalar(transaction, StoredProcedureName, parameters);

        return obj;
    }


    /// <summary>
    /// Runs the specified connectionstring.
    /// </summary>
    /// <param name="connectionstring">The connectionstring.</param>
    /// <returns></returns>
    public override DataSet Run(string connectionstring)
    {
        DataSet ds = SqlHelper.ExecuteDataset(connectionstring,
            CommandType.StoredProcedure, StoredProcedureName);

        return ds;

    }


    /// <summary>
    /// Runs this instance.
    /// </summary>
    public override void Run()
    {
        SqlHelper.ExecuteNonQuery(ConnectionString,
            CommandType.StoredProcedure, StoredProcedureName, Parameters);
    }


    /// <summary>
    /// Runs the specified parameters.
    /// </summary>
    /// <param name="commandType">Type of the command.</param>
    /// <param name="commandText">The command text stored procedure or sql queries.</param>
    /// <returns></returns>
    public override DbDataReader Run(CommandType commandType, string commandText)
    {
        SqlDataReader dr = SqlHelper.ExecuteReader(ConnectionString, CommandType.Text, commandText);

        return dr;

    }


    public SqlParameter[] Parameters { get; set; }

}

 

       我们很快就完成了SqlDataProvider类,这是由于我们把具体的实现都放在了SqlHelper中。在SqlHelper中包含具体的数据库操作方法,在其中包含了一些烦琐的数据库操作方法,如果我们说要让大家自己去完成这个Helper类,那么肯定是一个很痛苦的过程,而且严重影响了开发的效率,考虑到数据库操作方法的可重用性微软已经给我们提供了对SqlServer操作的Helper类(包含C#和VB)。

       现在SqlDataProvider类基本完成了,接着我们创建一个DataProviderManager类,在它里面提供工厂方法用来创建提供者对象。

 provider4

图 6 DataProviderManager设计

 

       在DataProviderManager类中的CreateProvider方法负责创建提供者对象(如:SqlDataProvider和OracleDataProvider)。

 

/// <summary>
/// The factory method to creates the provider instance.
/// </summary>
/// <returns>The instances of provider.</returns>
public static object CreateProvider()
{
    LoadProviders();

    return _provider;
}

/// <summary>
/// Loads the providers by webconfig setting.
/// </summary>
private static void LoadProviders()
{
    // providers are loaded just once
    if (null == _providers)
    {
        // Synchronize the process of loading the providers
        lock (SyncLock)
        {
            // Double confirm that the _provider is still null.
            if (null == _provider)
            {
                try
                {
                    // Reads the webconfig file corresponding node.
                    DataProviderSection section = (DataProviderSection)
                                                  WebConfigurationManager.GetSection(
                                                  "system.web/dataProviderService");

                    _providers = new DataProviderCollection();

                    // Creates provider instance, and invokes ProviderBase's Initialize function.
                    ProvidersHelper.InstantiateProviders(section.Providers, Providers, typeof(DataProvider));

                    // Gets the default in the collection.
                    _provider = Providers[section.DefaultProvider];
                }
                catch
                {
                    throw new ProviderException("Can't create instance");
                }
            }
        }
    }
}

 

       通过上面的代码我们hard code获取提供者信息的配置文件节点,那么DataProviderManager类根据配置文件设置来实例化提供者对象,接下来让我们设置配置文件。

 

 provider5

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值