最近跟一些朋友討論關於原廠模式的一些東西,覺得很有必要寫篇東西把抽象工廠的應用方法寫清楚。GoF在抽象工廠一章中沒有花很多篇幅在如何應用抽象工廠上。所幸ADO.NET為我們提供了一個非常標準的抽象工廠的例子,同時也是非常實用的一個介面——DbProviderFactory
我希望借一個利用DbProviderFactory重構網站的例子來回答兩個關於抽象工廠的常見問題:
- 為什麼要使用抽象工廠
- 如何使用抽象工廠
首先我們來看看這個重構之前的網站,這是我花1個小時寫的一個很具有代表性的例子,一個資料庫表,增刪改查四個頁面,查詢頁面用了控制項我們可以不用管它,為了示範,增刪改我沒有使用FormView。類似這樣的網站隨處可見,當然這並不是一個好的例子,沒有異常處理,也沒有任何封裝,只是一個供大家理解的例子而已。
這是一個依賴於SqlSever Client的網站,我們無法把它部署在其它類型的資料庫上。現在讓我們通過重構讓它支援多種資料庫。
使用抽象工廠之前,首先要讓代碼符合OO的基本原則之一,依賴倒轉原則。所以我們把所有的命名空間System.Data.SqlClient全都換成System.Data.Common,相應的類也換成以Db開頭的介面。然而不幸的是,當我們換完之後,會發現有語法錯誤:
這些討厭的new,我們沒有辦法建立一個介面的執行個體,所以new就沒有辦法不依賴具體的類。這樣,我們就不得不將原來的命名空間加回去了,而且也無法做到針對多種資料庫編程。到這裡,你大概已經可以猜到解決方案跟工廠有關了。這就是我們使用工廠的原因:建構函式不能多態。
接下來我們回到比較具體的問題,既然是用工廠重構可以解決問題,如何做呢?當然是優先使用.net類庫提供的抽象工廠類:DbProviderFactory。既然有了類,然後我們面臨的具體問題就是:何時建立工廠?儲存工廠的變數放在什麼地方?
讓我們來考慮一下工廠的在所用範圍:這是一個很簡單Web應用,我們只需要一個資料庫,所以決定我們使用資料庫類型的工廠應該是全域的,在web應用程式啟動時就建立。那麼這個位置顯然應該是Global類的靜態屬性。
這裡要順帶提一下Global類如何建立,按右鍵我們的網站root,然後add new Item 中所示選擇Global Application class就可以了。
之後我們會得到Global.asax檔案,其中包含Global類。之後我們在Global類中加入DbProvider,並且在Application_Start中建立它的具體類型。
comments:有朋友提出 在Application_Start中建立可能會有問題 See #20樓
有了DbProvider之後,我們可以把原有的代碼重構為依賴DbProvider建立資料庫相關對象的形式了,這裡仍然帖Update的例子:
這個時候,Update.aspx.cs不再需要包含System.Data.SqlClient等具體資料庫類型相關的命名空間了,成為了徹底不依賴具體資料庫類型的類。
用同樣的方法重構delete.aspx.cs和insert.aspx.cs之後,我們的整個系統就是與資料庫解耦的系統了,只需要在Global.asax中一處建立正確的工廠,並且在設定檔中寫好連接字串,就可以工作在任何資料庫中。
最後請看完整重構之後的代碼。
That's all. 謝謝觀賞。