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行都没敲出来。毕业后,匆匆忙忙找个培训公司培训,然后去工作,拖控件,重复性工作,长时间加班,一切的实现压碎了我们的理想。但我想对您和我自己说,人应该有梦想,应该让世界因为我们的存在而有一点的的确确的不同。希望您和我在看到这句话的时候,能摈弃以前的借口,多学习学习,成长为一名合格的程序员,而不是码农、码畜。

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

  • 相关阅读:
    Count and Say leetcode
    Find Minimum in Rotated Sorted Array II leetcode
    Find Minimum in Rotated Sorted Array leetcode
    Search in Rotated Sorted Array II leetcode
    search in rotated sorted array leetcode
    Substring with Concatenation of All Words
    Subsets 子集系列问题 leetcode
    Sudoku Solver Backtracking
    Valid Sudoku leetcode
    《如何求解问题》-现代启发式方法
  • 原文地址:https://www.cnblogs.com/SunnyZhu/p/3854454.html
Copyright © 2011-2022 走看看