0

我编写了一个用于访问数据库的 DLL。因此,我有一个名为IDbInterop的接口,如下所示:

public interface IDbInterop
    {
        void ExecuteCommandNonQuery(string commandText, params IDbParameter[] commandParameter);
        object ExecuteCommandScalar(string commandText, params IDbParameter[] commandParameter);
        DataSet ExecuteCommandDataSet(string commandText, params IDbParameter[] commandParameter);
    }

为了为特定的数据库提供程序获取此接口的实例,我引入了一个工厂,它以枚举作为参数来决定应该创建哪个具体实现:

public static class DbInteropFactory
{
    public static IDbInterop BuildDbInterop(DbType dbType, string connectionString)
    {
        switch (dbType)
        {
            case DbType.MSSQL:
                return new MSSQLDbInterop(connectionString);
            default:
                throw new ArgumentOutOfRangeException("dbType");
        }
    }
}

public enum DbType
{
    MSSQL,
}

到目前为止,我只为 MSSQL 数据库实现了一个具体的实现。现在,如果应该添加另一个数据库提供程序,我将不得不执行以下步骤:

  • 为具体实现创建一个类(例如 MySqlDbInterop)
  • 扩展枚举(例如 MYSQL)
  • 扩展工厂以允许用户获得新的实现

有没有一种方法,如果添加了新的实现,我不必扩展枚举和工厂?

4

2 回答 2

1

是的,至少我知道三种方式。

  1. 您可以使用Reflection实例化具体类,但您可能必须处理任何性能问题。
  2. 您可以让具体类向工厂注册自己,但您需要确保在任何客户端请求其实例之前进行注册。
  3. 您可以使用任何可用的IoC 容器,它使用依赖注入(构造函数或设置器注入)原理来为您实例化具体类。这些 IoC 容器在内部可以再次使用,Reflection如第一点所述。
于 2013-08-27T16:14:38.080 回答
0

是的,有办法。使用工厂工人。工人创建一个实例并具有匹配参数的知识。

public interface IFactoryWorker
{
   IDbInterop CreateInterop( string connectionString );
   bool AcceptParameters( string ProviderName );
}

一个示例工人将是

public class SqlServerFactoryWorker : IFactoryWorker
{
    public IDbInterop CreateInterop( string connectionString )
    {
      return new MSSQLDbInterop(connectionString);       
    }

    public bool AcceptParameters( string providerName )
    {
       return providerName == "System.Data.SqlClient";
    }
}

然后你的工厂就变成了

public static class DbInteropFactory
{
  private static List<IFactoryWorker> _workers;

  static DbInteropFactory()
  {
      _workers = new List<IFactoryWorker>();
      _workers.Add( new SqlServerFactoryWorker() );
  }

  public static void AddWorker( IFactoryWorker worker )
  {
      _workers.Add( worker );
  }

  public static IDbInterop BuildDbInterop( 
     string ProviderName, string connectionString)
  {
     foreach ( var worker in _workers )
     {
        if ( worker.AcceptParameters( ProviderName ) )
           return worker.CreateInterop( connectionString );

        // or return null 
        throw new ArgumentException();
     }
  }

这种方法有以下好处:

  • 将字符串作为提供者类型意味着您不必扩展任何枚举
  • 在工厂外拥有工人意味着您永远不必改变工厂本身
  • 创建一个新的提供者意味着您创建一个新的工人并在工厂中注册它,这可以在工厂之外使用AddWorker方法完成

这种方法遵循开闭原则。

为了简化代码,您可以让工作人员成为实际的互操作提供者。这意味着工作接口应该继承IDbInterop并实现所有逻辑。在这种方法中,工厂尝试定位“worker-provider”(foreach ( var worker in _workers)并在找到时返回它:

public interface IFactoryWorker : IDbInterop
{
   IDbInterop CreateInterop( string connectionString );
   bool AcceptParameters( string ProviderName );
}

     ...
     foreach ( var worker in _workers )
     {
        if ( worker.AcceptParameters( ProviderName ) )
           return worker;

        // or return null 
        throw new ArgumentException();
     }

这伴随着违反单一责任原则的代价。

于 2013-08-27T15:53:09.360 回答