应用程序有时需要适应于不同的数据库,例如要将一个Access数据库的网站改成使用Sql Server数据库。由于SQL Server和Access在ADO.Net上的使用是不同的,SQL Server使用的是System.Data.SqlClient命名空间下的SqlConnection、SqlCommand、SqlParameter、SqlDataReader、SqlDataAdapter,而Access则使用的是System.Data.Oledb命名空间下的相应的类。所以需要大量的查找、替换工作,这不是一个聪明的方式。如何使用软件设计模式,以不变应万变,保证代码的修改量是最小的呢?最基本的思想就是:将相同部分的代码提取出来,做成单独的类或接口,通过增加层次来提高代码的重用性。
例如,对于Access和SQL Server,都需要使用一段类似的代码来创建数据库的连接。
Access的格式
using System;
using System.Data;
using System.Data.Oledb;
…
private OledbConnection conn;
private void OpenConnection()
{
string strConnection;
conn=new System.Data.Oledb.OledbConnection(strConnection)
}
Sql Server 的格式
using System;
using System.Data;
using System.Data.Oledb;
…
private OledbConnection conn;
private void OpenConnection()
{
string strConnection;
conn=new System.Data.Oledb.OledbConnection(strConnection)
}
注意:除了上面黑体部分不同之外,其他代码都一模一样。如果能将这些黑体文字替换成一种对于Access、Sql Server都通用的Connection对象,具体使用什么数据库,系统只需要作一个简单设置,如通过Web.Config设置或定义一个字符串指定数据库的类型,那多方便啊。
抽象工厂模式(Abstract Factory Pattern)简介
抽象工厂模式有下面的一些优点和缺点:
1) 它分离了具体的类。Abstract Factory模式帮助你控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程,它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体工厂的实现中被分离;它们不出现在客户代码中。
2) 它使得易于交换产品系列一个具体工厂类在一个应用中仅出现一次—即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。在我们的用户界面的例子中,我们仅需转换到相应的工厂对象并重新创建接口,就可实现从M o t i f窗口组件转换为Presentation Manager窗口组件。
3) 它有利于产品的一致性当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一个系列中的对象,这一点很重要。而A b s t r a c t F a c t o r y很容易实现这一点。
4) 难以支持新种类的产品难以扩展抽象工厂以生产新种类的产品。这是因为A b s t r a c t F a c t o r y接口确定了可以被创建的产品集合。支持新种类的产品就需要扩展该工厂接口,这将涉及A b s t r a c t F a c t o r y类及其所有子类的改变。
具体代码实现
1、定义抽象工厂类(DbProviderFactories.cs)。
抽象工厂类需要返回多个具体类(多种数据库)中的一个。所以GetFactory()返回的类型是一个具体的工厂类。
using System;
using System.Web;
using System.Configuration;
using System.Data;
using System.Data.Common;
using Microsoft.Win32;
namespace SplendidCRM
{
/// <summary>
/// Summary description for DbProviderFactories.
/// </summary>
public class DbProviderFactories
{
public static DbProviderFactory GetFactory()
{
// 从Application中获取信息
HttpApplicationState Application = HttpContext.Current.Application;
string sSplendidProvider = Sql.ToString(Application["SplendidProvider"]);
string sConnectionString = Sql.ToString(Application["ConnectionString"]);
// 如果在Application没有信息,则从Web.Config中获取。
/*
<configuration>
<appSettings>
<add key="SplendidProvider" value="System.Data.SqlClient" />
<add key="SplendidSQLServer" value="Password=123;User ID=sa;Initial Catalog=DB;Data Source=WWW;Packet Size=4096" />
</appSettings>
*/
if ( Sql.IsEmptyString(sSplendidProvider) || Sql.IsEmptyString(sConnectionString) )
{
sSplendidProvider = ConfigurationSettings.AppSettings["SplendidProvider"];
switch ( sSplendidProvider )
{
case "System.Data.SqlClient":
sConnectionString = ConfigurationSettings.AppSettings["SplendidSQLServer"];
break;
case "System.Data.OracleClient":
sConnectionString = ConfigurationSettings.AppSettings["SplendidSystemOracle"];
break;
case "Oracle.DataAccess.Client":
sConnectionString = ConfigurationSettings.AppSettings["SplendidOracle"];
break;
}
Application["SplendidProvider"] = sSplendidProvider;
Application["ConnectionString"] = sConnectionString;
}
return GetFactory(sSplendidProvider, sConnectionString);
}
///
public static DbProviderFactory GetFactory(string sSplendidProvider, string sConnectionString)
{
switch ( sSplendidProvider )
{
case "System.Data.SqlClient":
{
return new SqlClientFactory(sConnectionString);
}
case "System.Data.OracleClient":
{
return new OracleSystemDataFactory(sConnectionString);
}
case "Oracle.DataAccess.Client":
{
return new OracleClientFactory(sConnectionString);
}
default:
throw(new Exception("不支持的factory " + sSplendidProvider));
}
}
}
}