在这边博客设计模式学习(一)中,我分别写了操作Sql Server与Oracle数据库的DBHelper两个帮助类(一个GetTable()方法),但我认为不方便,于是开始研究是否可以只写一个类,就可以操纵两种数据库呢,于是便开始了分析。
经过分析,在查看OracleConnection与SqlConnection两个类时,有如下的发现:
public sealed class SqlConnection : DbConnection, ICloneable
public sealed class OracleConnection : DbConnection, ICloneable
这让我十分的惊异,因为他们都继承DbConnection类,更让我惊异的是DbConnection类是一个抽象类,而且实现了IDbConnection接口,代码如下:
public abstract class DbConnection : Component, IDbConnection, IDisposable
不仅如此,SqlCommand与OracleCommand都 继承DBCommand,
SqlDataAdapter与OracleDataAdapter都继承DbDataAdapter, IDbDataAdapter, IDataAdapter。
因此,我觉得这应该是net框架里面一种的模式,而且很有可能是抽象工厂模式,而不是工厂模式(工厂模式解决的是某个对象的创建工作,这个对象面临着剧烈的变化,但是拥有比较稳定的接口,因此,我们可以使其成为抽象类,最后让一个子类具体去实现这个抽象类),这里的DBConnection、DBCommand、DBDataAdapter、DBDataReader四个类的实现应该是工厂模式(如果理解有误,请指正)。
那么有了工厂模式会什么还会有抽象工厂模式呢?这是因为简单工厂模式不能应对不同系列对象的变化。抽象工厂模式的动机与意图如下:
动机:在软件系统中,经常面临着一系列相互依赖的对象的创建工作,同时,由于需求的变化,往往存在更多系列对象的创建工作。
意图:提供一个接口,让该接口负责创建一系列“相关或者相互依赖的对象”,无需制定它们具体的类。(GOF)
在这里,Connection、Command、DataAdapter、DataReader四个对象,是相互依赖的,可以操作Sql Server、Oracle、Access等数据库。因此,根据上面的动机与意图,可以判定这是抽象工厂模式。
请看下图:
在这张图左上角有一个工厂DbProviderFactories,这个静态类可以通过参数为抽象工厂DbProviderFactory提供一个具体的工厂,即可以使SqlClientFactory,也可以是OracleClientFactory工厂。有了DbProviderFactory这个抽象工厂,那么我们就可以只写一个类,就可以根据配置文件来得到具体的工厂,通过一个具体的工厂,可以操作具体的数据库,这样我的目标就实现了(这个DbProviderFactories抽象工厂并没有实现MySqlClientFactory,如果要与MySql数据库交互,则需自己想办法了)。
只需写一个类,就可操作两种不同的数据库,或者多种数据库,多么方便啊。
新建一个DataDBHelper,这里写了一个GetDataTable()示例方法获得表数据,代码如下:
public class DataDBHelper { private string providerName="";//数据库提供者 private string strConn="";//连接数据库字符串 DbProviderFactory factory = null;//工厂提供者 public DataDBHelper() { providerName = ConfigurationManager.AppSettings["DBProvider"].ToString(); //如果是Sql Server数据库配置文件则是 System.Data.SqlClient 如果是Oracle数据库 则是System.Data.OracleClient strConn = ConfigurationManager.ConnectionStrings["conStr"].ConnectionString; factory = DbProviderFactories.GetFactory(providerName);//创建工厂 }
public DataTable GetDataTable(string strSql, params DbParameter[] param) { using (DbConnection conn = factory.CreateConnection())//创建连接 { conn.ConnectionString = strConn; //赋予连接字符串 using (DbCommand cmd = factory.CreateCommand()) {//创建Sql命令 cmd.CommandText = strSql; //赋值Sql命令语句 cmd.Connection = conn; DbParameter dbParam = factory.CreateParameter(); //参数赋值 if (param.Length > 0) { for (int i = 0; i < param.Length; i++) { dbParam.ParameterName = param[i].ParameterName; dbParam.Value = param[i].Value; } cmd.Parameters.Add(dbParam); } DbDataAdapter adapter = factory.CreateDataAdapter(); //创建适配器 adapter.SelectCommand = cmd; DataTable dt = new DataTable(); conn.Open(); adapter.Fill(dt); conn.Close(); return dt; } } }
其中,SQL Server数据库的配置文件如下:
<connectionStrings> <add name="conStr" connectionString="Data Source=xianrongbin-pc;Initial Catalog=TestFactory;User Id=sa; Password=123456;"/> </connectionStrings> <appSettings > <add key="DBProvider" value="System.Data.SqlClient"/> </appSettings>
Oracle数据库的配置文件如下
<connectionStrings>
<add name="conStr" connectionString="data source=orcl;User Id=scott;Password=m123;">
</connectionStrings>
<appSettings >
<add key="DBProvider" value="System.Data.OracleClient"/>
</appSettings>
那么如何调用GetDataTable()这个方法呢,我想,应该是很好调用的,示例代码如下
DataDBHelper dbHelper = new DataDBHelper();DataTable dtOracleInfo = dbHelper.GetDataTable( " select * from FactoryTable where id>@ID ", parms); //Oracle调用
SqlParameter parms = new SqlParameter("@ID", "2"); DataTable dtSqlInfo = dbHelper.GetDataTable("select * from FactoryTable where id>@ID", parms); //SQl Server调用
OracleParameter param =new OracleParameter(":empno","7369");select ename from emp where empno=:empno
从上面的代码我们可以看到,原本需要写两个数据库帮助类,现在只需要写一个类了,减少了代码的数量;其次,在数据层中,我们只需要new一个DataDBHelper类,往其传入相应的参数,然后就可以调用对应的方法得到对应的值,而不需要去判断new SqlHelper()还是new OracleHelper()。
使用Abstract Factory 模式需要注意下面几个要点:
1、如果没有应对“多系列对象构建”的需求变化,则没有必要使用此模式,只有一个对象的需求变化,就完全可以使用静态工厂模式。
2、“系列对象指的是这些对象之间有相互依赖或作用的关系。如DbConnection与DbCommand。
3、Abstract Factory模式主要在于应对“新系列”的需求变动,其缺点在于难以应对“新对象”的需求变动。
4、Abstract Factory常常与工厂模式共同组合应对“对象创建”的需求变动。微软的例子我们可以明显看到,DBConnection本身就是一种工厂模式。
虽然搞定了DBHelper,但是如何方便的让程序在两种数据库之间切换,仍然是一个值得思考的问题,请关注我以后的博文。