看到標題您一定很疑惑,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 }

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 DBA15 {16 public DBA()17 {18 //19 // TODO: Add constructor logic here20 //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 try34 {35 DataSet ds = new DataSet();36 objAdapter.Fill(ds, "dt");37 return ds.Tables["dt"];38 }39 catch (SqlException e)40 {41 //這里面寫可以寫ErrorLog42 }43 }44 45 46 }
上面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 }

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 DBAStore11 {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 : DBAStore21 {22 private string _aConnection= "A公司連接字符串";23 private string _bConnection = "B公司連接字符串";24 private string _cConnection = "C公司連接字符串";25 26 27 28 public string CConnection29 {30 get { return _cConnection; }31 set { _cConnection = value; }32 }33 34 public string BConnection35 {36 get { return _bConnection; }37 set { _bConnection = value; }38 }39 40 41 public string AConnection42 {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 }
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 DBHelp10 {11 public class GetDataTable:SqlStatement12 {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 try33 {34 DataSet ds = new DataSet();35 sqlDataAdapter.Fill(ds, "dt");36 return ds.Tables["dt"];37 }38 catch39 {40 return null;41 }42 finally43 {44 sqlConnection.Close();45 }46 }47 }48 }
通過以上三個類,我們就可以通過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行都沒敲出來。畢業后,匆匆忙忙找個培訓公司培訓,然后去工作,拖控件,重復性工作,長時間加班,一切的實現壓碎了我們的理想。但我想對您和我自己說,人應該有夢想,應該讓世界因為我們的存在而有一點的的確確的不同。希望您和我在看到這句話的時候,能擯棄以前的借口,多學習學習,成長為一名合格的程序員,而不是碼農、碼畜。
話有點多,有點啰嗦,而且語言不清晰,邏輯混亂,請您見諒。
新聞熱點
疑難解答