[ASP.NET]重構之路系列v3 – 跨專案使用類別庫

[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通知我們』。

先簡單列出,我們要設計的功能有哪些:

  1. 我們要先取得要比對的帳號密碼清單(正規的設計,密碼資料應經過hash處理)
  2. 與Authetication的資料比對
  3. 記錄下來比對不一致的帳號資料後,mail給相關人員。


設計步驟
步驟一:
我們用v2 top-down的方式,先將我們要處理的事情用註解寫出來,並檢查是否有哪邊有遺漏。

batch要做的事 

我們可以發現,第二件要做的事,跟我們之前網站所設計的service是同一件事,那麼我們該怎麼做呢?將網站的App_Code底下分類好的資料夾都copy一份到這個console project? No! No! No! 這就跟我們之前說的一樣了,每次的複製貼上,都可能會造成技術債的產生。一式多份的問題,會對未來系統的發展與維護造成一定的傷害。

步驟二:
我們將網站中App_Code底下分類好的程式,移至我們新建立的類別庫(Library)中。當類別庫建置完畢時,會產生對應的組件,也就是DLL檔。我們的網站跟console project只需要參考這一顆dll,就可以使用同一份service與dao了。

我們在方案底下,新增一個專案,選擇『類別庫』,給它個名字『Joey.Model』

image

接著我們網站與console project加入Joey.Model這個參考。可以選擇加入『建置完成的dll』,或是直接『加入專案參考』,這邊我們為了眼前開發方便,先加入專案參考。(實際情況,進去版本控管的project會建議參考dll,並透過版本號,比較不會造成開發團隊混淆或困擾)。
image
image
image

加完參考,建置整個方案之後,會看到我們的WebSite跟console project,都產生了一個bin的資料夾,裡面有Joey.Model這個dll。
image 

步驟三:
接著,我們將原本在App_Code底下的資料夾,搬過去Joey.Model底下,並給予對應的namespace。

image

改完namespace後,建置會發現,因為namespace的改變,我們需要再調整一下程式,告訴complier我們要用的class是哪一個。
image

一樣,在紅色毛毛蟲上面,按下『Ctrl+.』,就可以透過IDE來選擇我們要using的namespace。
image

當類別庫調整完namespace,建置應該就會成功。這個時候,我們仍然完全沒動到原本service與dao的邏輯。接著,我們用同樣的方式,將原本的WebSite中使用到AuthenticationService的程式,也加上對應的namespace。
image 

到這邊,我們對原本網站的重構就告一段落了,一切程式碼邏輯、功能跟原本一模一樣。我們只是將App_Code裡面的程式,抽到了新的類別庫裡面,將Business logic與Data access的部分,徹底的與呈現端(這邊就是我們的網站或批次)分離開來。

步驟四:
回到我們的批次程式,我們一樣練習著top-down的方式來撰寫程式,將註解對應的方法列出來。稍微不一樣的是,我們要開始在設計的時候就考慮,這個方法是屬於什麼樣的Service,也就是歸哪一個職責所管轄。

假設我們取得要比對的資料,是透過一個SyncService來取得。我們一樣用『產生新型別』的方式,讓Visual Studio自動去幫我們產生對應的class。

image 

第二個部分,我們就透過AuthenticationService來處理。將大致上的程式top-down寫好後,將紅色毛毛蟲的部分都透過『產生』的功能來產生對應的class與property。

image 

在Profile中就會看到對應的property,而且會自動幫我們定義好型別。(因為是從AuthenticationService的VerifyPasswordById方法中產生的)

image

到這邊,我們的高階邏輯就撰寫完畢了,其他的就都只是填入方法內容而已。

    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版本才有),我們來檢視一下我們批次程式與類別庫的關係(點圖可見大圖): 

image

最後我們的架構就會長的如同下圖一樣:

最後的價構圖

結論
這篇文沒有什麼太高深的技巧,要帶給各位的是:

  1. 反覆練習top-down的設計方式,抽象地去設計我們需要的功能,不要被細節給蒙蔽。
  2. 商業邏輯與資料存取,通常會被多個專案或網站所使用,這邊簡單的介紹類別庫該怎麼新增與設定參考。這邊取名為Model,其實就是MVC pattern中的M,包含了Service與Data access的部分。(如果您設計的MVC裡的Model只有Data Access,那就是Controller包含了Service,這樣的分法會讓Controller負責太多職責而失去Controller這個角色的意義。如果您Model裡面Service與Data access沒有分開,那就跟v1的糾結原型版只重構了一半一樣糟糕)
  3. 類別庫會是Unit Test的一個很重要的基底,請一定要熟悉這樣的設計方式。


Sample Code: RefactoringSample-v3.zip

 


blog 與課程更新內容,請前往新站位置:http://tdd.best/