[ASP.NET]重構之路系列v3 – 跨專案使用類別庫
前言
從v1開始,介紹了原型糾結版,怎麼樣從糾結成一團的程式碼,將UI、Service與Dao的觀念獨立開來(請參考:[ASP.NET]重構之路系列v1 – UI, Business logic, Data access概念分開)。接著v2介紹了當同一個網站中,其他網頁也需要用到相同的邏輯時,我們避免重複開發相同的程式碼,避免了一式多份,將Service與Dao的操作,封裝到各自負責的class之中,讓class的職責分明,呼叫端需要處理什麼樣的事情,則交給負責該職責的class來處理(請參考:[ASP.NET]重構之路系列v2 – DRY & Top-Down思考方式)。
接著,我們面對的需求也是很常見的,例如這一段Authentication的商業邏輯與資料存取,需要在其他專案上用到。(或許是另一個網站、另一個類別庫或是另一個console project,甚至於Winform、Silverlight或是ASP.NET MVC website)。這時候v2將處理business logic的service與Data Access的dao,放在自己的web site裡面,就會造成一樣的問題:重複開發、一式多份。
這一篇文章要介紹的就是,如何將service與dao設計在類別庫裡面,來解決跨專案使用的問題。(ASP.NET的server control就是一個最好的例子)
需求說明
PM提出來一個需求:『我們已經有一支程式負責將帳號密碼從Authentication同步到另一個資料庫,但user反應同步的資料有時候會出現不齊全的情況,這對我們來說很困擾。能不能請你寫一支定時執行的批次程式,幫我們檢查某一群帳號是否有密碼不一致的情況,若有,請發mail通知我們』。
先簡單列出,我們要設計的功能有哪些:
- 我們要先取得要比對的帳號密碼清單(正規的設計,密碼資料應經過hash處理)
- 與Authetication的資料比對
- 記錄下來比對不一致的帳號資料後,mail給相關人員。
設計步驟
步驟一:
我們用v2 top-down的方式,先將我們要處理的事情用註解寫出來,並檢查是否有哪邊有遺漏。
我們可以發現,第二件要做的事,跟我們之前網站所設計的service是同一件事,那麼我們該怎麼做呢?將網站的App_Code底下分類好的資料夾都copy一份到這個console project? No! No! No! 這就跟我們之前說的一樣了,每次的複製貼上,都可能會造成技術債的產生。一式多份的問題,會對未來系統的發展與維護造成一定的傷害。
步驟二:
我們將網站中App_Code底下分類好的程式,移至我們新建立的類別庫(Library)中。當類別庫建置完畢時,會產生對應的組件,也就是DLL檔。我們的網站跟console project只需要參考這一顆dll,就可以使用同一份service與dao了。
我們在方案底下,新增一個專案,選擇『類別庫』,給它個名字『Joey.Model』
接著我們網站與console project加入Joey.Model這個參考。可以選擇加入『建置完成的dll』,或是直接『加入專案參考』,這邊我們為了眼前開發方便,先加入專案參考。(實際情況,進去版本控管的project會建議參考dll,並透過版本號,比較不會造成開發團隊混淆或困擾)。
加完參考,建置整個方案之後,會看到我們的WebSite跟console project,都產生了一個bin的資料夾,裡面有Joey.Model這個dll。
步驟三:
接著,我們將原本在App_Code底下的資料夾,搬過去Joey.Model底下,並給予對應的namespace。
改完namespace後,建置會發現,因為namespace的改變,我們需要再調整一下程式,告訴complier我們要用的class是哪一個。
一樣,在紅色毛毛蟲上面,按下『Ctrl+.』,就可以透過IDE來選擇我們要using的namespace。
當類別庫調整完namespace,建置應該就會成功。這個時候,我們仍然完全沒動到原本service與dao的邏輯。接著,我們用同樣的方式,將原本的WebSite中使用到AuthenticationService的程式,也加上對應的namespace。
到這邊,我們對原本網站的重構就告一段落了,一切程式碼邏輯、功能跟原本一模一樣。我們只是將App_Code裡面的程式,抽到了新的類別庫裡面,將Business logic與Data access的部分,徹底的與呈現端(這邊就是我們的網站或批次)分離開來。
步驟四:
回到我們的批次程式,我們一樣練習著top-down的方式來撰寫程式,將註解對應的方法列出來。稍微不一樣的是,我們要開始在設計的時候就考慮,這個方法是屬於什麼樣的Service,也就是歸哪一個職責所管轄。
假設我們取得要比對的資料,是透過一個SyncService來取得。我們一樣用『產生新型別』的方式,讓Visual Studio自動去幫我們產生對應的class。
第二個部分,我們就透過AuthenticationService來處理。將大致上的程式top-down寫好後,將紅色毛毛蟲的部分都透過『產生』的功能來產生對應的class與property。
在Profile中就會看到對應的property,而且會自動幫我們定義好型別。(因為是從AuthenticationService的VerifyPasswordById方法中產生的)
到這邊,我們的高階邏輯就撰寫完畢了,其他的就都只是填入方法內容而已。
class Program
{
static void Main(string[] args)
{
//1.取得欲比對帳號密碼的清單
SyncService syncService = new SyncService();
IList<Profile> profileCollection = syncService.GetCheckList();
//2.檢查是否與Authentication的資料一致
StringBuilder mailContent = new StringBuilder();
mailContent.AppendLine(string.Format("檢查時間:{0}", System.DateTime.Now.ToString()));
AuthenticationService authenticationService = new AuthenticationService();
bool isAnyError = false;
foreach (var item in profileCollection)
{
if (authenticationService.VerifyPasswordById(item.Id, item.Password) != VerifyStatus.Passed)
{
//3.將不一致的資料記錄下來
string message = string.Format("同步異常的帳號:{0}", item.Id);
mailContent.AppendLine(message);
isAnyError = true;
}
}
//4.如果有不一致的資料,則發送通知
if (isAnyError)
{
AlertService alertService = new AlertService();
alertService.Alert(mailContent.ToString());
}
}
}
透過Visual Studio 2010產生Sequence diagram的功能(Ultimate版本才有),我們來檢視一下我們批次程式與類別庫的關係(點圖可見大圖):
- 反覆練習top-down的設計方式,抽象地去設計我們需要的功能,不要被細節給蒙蔽。
- 商業邏輯與資料存取,通常會被多個專案或網站所使用,這邊簡單的介紹類別庫該怎麼新增與設定參考。這邊取名為Model,其實就是MVC pattern中的M,包含了Service與Data access的部分。(如果您設計的MVC裡的Model只有Data Access,那就是Controller包含了Service,這樣的分法會讓Controller負責太多職責而失去Controller這個角色的意義。如果您Model裡面Service與Data access沒有分開,那就跟v1的糾結原型版只重構了一半一樣糟糕)
- 類別庫會是Unit Test的一個很重要的基底,請一定要熟悉這樣的設計方式。
Sample Code: RefactoringSample-v3.zip
blog 與課程更新內容,請前往新站位置:http://tdd.best/