zoukankan      html  css  js  c++  java
  • "类工厂模式"改写SqlHelper

    看到标题您一定很疑惑,23种经典设计模式什么时候多了一个"类工厂模式",稍等,请听我慢慢道来。

    实践是检验真理的唯一途径。最近用了"类工厂模式"改写了我公司的SqlHelper类,改写了一大半了,拿出半成品和大家一起讨论。

    首先说下我们公司环境:我公司在ABC三地都有工厂,同时都有各自的DB。经过调研,ABC三地的很多网页都有可有整合在一起的地方,我负责整合三地网页。

    一开始,没接触设计模式的时候。我的Sql是这样写的:"select * from "+ strSite +".dbo.Table where Id='XXX'".Sql语句中的strSite是从URL中获得的公司别。

    这样可以通过strSite来区分ABC工厂的数据库。我是这样调用SqlHelper类的:DBA.GetDataTable("select * from "+ strSite +".dbo.Table where Id='XXX'");

    一个Sql中不可能只有一个表,每写一个表,我就要在前面加上"+ strSite +",如果这样整合下去。公用页面的逻辑复杂了,而且在调试Sql语句的时候更是麻烦,经常因为少些后面的架构,或者多写DB的名称,导致系统报错。

    知道了错误,就需要从错误中改变,我发现Sql中的strSite只是提供了一种环境,例如,strSite=A的时候,即为在A公司的DB中执行Sql,在B和C公司,同样是执行Sql的环境变了。

    那我可不可以传一个参数,在SqlConnection的时候,动态的选择不同的Sql环境。

    咱爷们说干就干,于是就有了下面的代码:

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Data.SqlClient;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 public class DBAStore
     9 {
    10     private static string AConnection = "A公司连接字符串";
    11     private static string BConnection = "B公司连接字符串";
    12     private static string CConnection = "C公司连接字符串";
    13 
    14 
    15     public SqlConnection OrderSqlConnection(string strSite)
    16     {
    17         return GetConnection(strSite);
    18     }
    19 
    20     // 获取SqlConnection方法
    21 
    22     private static SqlConnection GetConnection(string strSite)
    23     {
    24         switch (strSite)
    25         {
    26             case "A":
    27                 return new SqlConnection(AConnection);
    28             case "B":
    29                 return new SqlConnection(BConnection);
    30             case "C":
    31                 return new SqlConnection(CConnection);
    32             default:
    33                 return null;
    34         }
    35     }
    36 }
    DBAStore
     1 using System;
     2 using System.Data;
     3 using System.Data.SqlClient;
     4 using System.Configuration;
     5 using System.Collections.Generic;
     6 using System.Web;
     7 using System.Web.Security;
     8 using System.Web.UI;
     9 using System.Web.UI.WebControls;
    10 using System.Web.UI.WebControls.WebParts;
    11 using System.Web.UI.HtmlControls;
    12 using System.Collections;
    13 
    14 public class DBA
    15 {
    16     public DBA()
    17     {
    18         //
    19         // TODO: Add constructor logic here
    20         //
    21     }
    22     /// <summary>
    23     /// 执行Sql返回DataTable(新版)
    24     /// </summary>
    25     /// <param name="strSite">公司别</param>
    26     /// <param name="strSql">SQLStatement</param>
    27     /// <returns></returns>
    28     public static DataTable GetDataTable(string strSite, string strSql)
    29     {
    30         DBAStore dbaStore = new DBAStore();
    31         SqlConnection objConn = dbaStore.OrderSqlConnection(strSite);
    32         SqlDataAdapter objAdapter = new SqlDataAdapter(strSql, objConn);
    33         try
    34         {
    35             DataSet ds = new DataSet();
    36             objAdapter.Fill(ds, "dt");
    37             return ds.Tables["dt"];
    38         }
    39         catch (SqlException e)
    40         {
    41             //这里面写可以写ErrorLog
    42         }
    43     }
    44 
    45 
    46 }
    DBA

    上面2个类就可以完成我所想的功能,因为用的是Static写的,所以可以直接用,于是就有了下面的Sql语句:"select * from Table where Id='XXX'",如果我想获得A公司的数据,就如下调用:DBA.GetDataTable("A",SqlStatemet);(SqlStatement即为前面黄色背景的Sql语句)

    拿着改写好的SqlHelper,向我师傅炫耀去了,师傅看过之后邹着眉头说,你这样写固然是有好处的,但是你将本来对数据库没有任何意义的公司别传入SqlHelper,增加了耦合度,虽然解决了现在的问题,但是对以后的拓展应该是不好的。(其实静态类就这点不好,实在无法完美支持SqlHelper,如果哪位师兄有好的方法,请一定不要吝啬指导我)

    好吧,第一次出师就不利,简单和师傅还有我们经理讨论了下未来SqlHelper的架构方向,一致决定在整合的页面中不再使用静态类去完成Sql语句,转而用工厂模式来写我们的SqlHelper.

    在《Head First》中,工厂模式用的是Pizza的例子来诠释的,有一个Pizza来决定如何做Pizza,有一个PizzaStore来决定如何做哪个地区的Pizza.

    于是我把SqlHelper的变化部分抽象出来,写一个抽象类SqlStatement,里面有GetConnection(获得不同公司的连接字符串)Result(获得不同的返回值,例如DataTa或者首行首列等)两种抽象方法,由SqlStatement来决定如何去执行一个Sql,另外写一个DBAStore来决定获得哪个公司的连接字符串。

    代码如下:

    类似于Pizza的抽象超类,实现如何执行Sql。DBAStore,功能和类名一样,抽象用来实现,决定获得哪个公司的连接字符串。

     1     public abstract class SqlStatement
     2     {
     3         public abstract SqlConnection GetSqlConnection();
     4 
     5         public abstract object Result(string strSql);
     6         //{
     7         //    //SqlConnection sqlConnection = GetSqlConnection(strSite);
     8         //    //return SqlCommand(strSql,get);
     9         //}
    10     }
    SqlStatement
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Data.SqlClient;
     4 using System.Linq;
     5 using System.Text;
     6 using System.Threading.Tasks;
     7 
     8 namespace DBHelp
     9 {
    10     public abstract class DBAStore
    11     {
    12         public SqlConnection OrderDBA(string strSite)
    13         {
    14             SqlConnection objConn;
    15             objConn = CreateDBA(strSite);
    16             return objConn;
    17         }
    18         public abstract SqlConnection CreateDBA(string strSite);
    19     }
    20     public class OA : DBAStore
    21     {
    22         private string _aConnection= "A公司连接字符串";
    23         private string _bConnection = "B公司连接字符串";
    24         private string _cConnection = "C公司连接字符串";
    25 
    26 
    27 
    28         public string CConnection
    29         {
    30             get { return _cConnection; }
    31             set { _cConnection = value; }
    32         }
    33 
    34         public string BConnection
    35         {
    36             get { return _bConnection; }
    37             set { _bConnection = value; }
    38         }
    39 
    40 
    41         public string AConnection
    42         {
    43             get { return _aConnection; }
    44             set { _aConnection = value; }
    45         }
    46 
    47         public override SqlConnection CreateDBA(string strSite)
    48         {
    49             switch (strSite)
    50             {
    51                 case "A":
    52                     return new SqlConnection(_aConnection);
    53                 case "B":
    54                     return new SqlConnection(_bConnection);
    55                 case "C":
    56                     return new SqlConnection(_cConnection);
    57                 default:
    58                     return null;
    59             }
    60         }
    61     }
    62 }
    DBAStore

    GetDataTable类,继承于SqlStatement,返回一个DataTable类型的dt

     1 using System;
     2 using System.Collections.Generic;
     3 using System.Data;
     4 using System.Data.SqlClient;
     5 using System.Linq;
     6 using System.Text;
     7 using System.Threading.Tasks;
     8 
     9 namespace DBHelp
    10 {
    11     public class GetDataTable:SqlStatement
    12     {
    13         private string _strSite;
    14 
    15         public GetDataTable(string strSite)
    16         {
    17             _strSite = strSite;
    18         }
    19 
    20         public override SqlConnection GetSqlConnection()
    21         {
    22             DBAStore dbaStore = new OA();
    23             SqlConnection sqlConnection = dbaStore.OrderDBA(_strSite);
    24             return sqlConnection;
    25         }
    26 
    27         public override object Result(string strSql)
    28         {
    29             SqlConnection sqlConnection = GetSqlConnection();
    30             SqlDataAdapter sqlDataAdapter = new SqlDataAdapter(strSql, sqlConnection);
    31             sqlConnection.Open();
    32             try
    33             {
    34                 DataSet ds = new DataSet();
    35                 sqlDataAdapter.Fill(ds, "dt");
    36                 return ds.Tables["dt"];
    37             }
    38             catch
    39             {
    40                 return null;
    41             }
    42             finally
    43             {
    44                 sqlConnection.Close();
    45             }
    46         }
    47     }
    48 }
    GetDataTable

    通过以上三个类,我们就可以通过New实例的方式来执行Sql,返回需要的数值。

    SqlStatement GetDataTable = new GetDataTable("A");//将DB环境切换到A公司,就像在SQL SERVER Management Studio中使用:"USE A"一样。
    GetDataTable.Result("select Site from Table where Id in ('XXX','OOO')");//在A公司环境下执行前面黄色背景的Sql语句。

    如果您需要返回一个Bool值去判断是否执行插入或者删除语句,那么就新建一个ExecSql,继承于SqlStatement,更改相应参数就可以获得是否执行成功。

    因为代码比较容易写,我就不再提供相应的类了,您可以自己试试,真的非常简单。

    但是,这时候问题又出来了,每次我使用不同方法执行Sql,或返回Bool值,或返回一个DataTable值,每次都得New一次,实在非常繁琐了,能不能像以前那样,直接用静态方法,实现类似DBA("A").GetDataTable("Sql语句")或者DBA("B").ExecSql("Sql语句")呢,亲爱的小伙伴,我也在朝这个方向努力,希望能和大家一起探讨如何写更简单的SqlHelper。

    文章写到最后,我来解释下,为什么我要用"类工厂模式"来形容个写的这个SqlHelper呢,聪明的小伙伴,您有没有发现,我在写DBAStore类的时候,在创建一个连接字符串的时候,用了一个Switch来返回不同公司的连接字符串,这和《Head First》中创建不同的确Pizza店返回的是Pizza类型的值,而我的DBAStore中没有更过度设计的返回一个SqlStatement类型。所以这点和工厂模式有点差异,为了纪念第一次理解工厂模式,就让我用这个"类工厂模式"给自己庆贺吧。

    写在最后:

    因为时间比较晚了,也准备将博文发给同事,让他们帮忙点赞,UML图,暂时就不提供了,后续会提供。

    可能您会问,为什么不提供一个完整的SqlHelper方法给大家使用?我的解释是,我其实也没有完全做到我梦想的方式。同时每天等QQ邮箱的时候,能看到有人给我上一篇文章评论的话语,感觉倍受鼓舞,如果长时间不更新博文,他们以为我失踪了,或者以为我放弃了程序员这个行业,为了他们,我选择将我这个半成品公布出来,让大家使用。

    关于代码能否使用:这个您可以放心,我是经过调试的,并且用在我们公司的正式环境的,是可以使用的,只是为了保密和安全性考虑,我截取的时候,可能会有误差,如果您Copy了代码,但是无法使用,可以联系我索取完整代码。

    关于程序员这条路:之前阅读过李开复的自传《让世界因你而不同》,真的被谷歌、微软等IT巨头的工作环境还有他们所写的酷炫的代码所吸引。回到现实,我们毕业于最低流大学,大学的时候压根不会去敲代码。即使院长希望我们能在4年中敲出20000行代码,实际上我们压根连200行都没敲出来。毕业后,匆匆忙忙找个培训公司培训,然后去工作,拖控件,重复性工作,长时间加班,一切的实现压碎了我们的理想。但我想对您和我自己说,人应该有梦想,应该让世界因为我们的存在而有一点的的确确的不同。希望您和我在看到这句话的时候,能摈弃以前的借口,多学习学习,成长为一名合格的程序员,而不是码农、码畜。

    话有点多,有点啰嗦,而且语言不清晰,逻辑混乱,请您见谅。

  • 相关阅读:
    java8
    java7
    java6
    java5
    java复习4
    学习笔记
    Reflection笔记
    通过Reflection来获得方法和信息
    學習反射2
    學習反射1
  • 原文地址:https://www.cnblogs.com/SunnyZhu/p/3854454.html
Copyright © 2011-2022 走看看