Visual Studio 2010層架構(gòu)驗證的實現(xiàn)
原創(chuàng)【51CTO獨家特稿】當(dāng)設(shè)計一個應(yīng)用程序時,基本的要求是所有的通信信息交互都要通過定義的接口進(jìn)行,不能讓應(yīng)用程序直接調(diào)用該接口的類別或方法。所以,可以通過Layer Diagram來展示這個架構(gòu)上的想法。51CTO向您推薦《用好Visual Studio 2010進(jìn)行層架構(gòu)設(shè)計》
我們將使用一段非常簡單的代碼,主要強(qiáng)調(diào)的是代碼所代表的概念,而不是代碼的細(xì)節(jié)。并將在現(xiàn)有代碼層關(guān)系架構(gòu)邏輯設(shè)計分析的基礎(chǔ)上進(jìn)行層驗證(Layer Validation)功能:
①打開Visual Studio 2010,通過Modeling Projects模板指定解決方案(Solution)的名稱為LayerValidation(File|New|Project命令,選擇ModelingProject,命名為LayerValidation),并提供創(chuàng)建一個名為“Client”的C#控制臺程序,單擊“OK”按鈕。
②在解決方案瀏覽器中,使用鼠標(biāo)右鍵單擊Solution節(jié)點,選擇“New Project…”命令,在彈出的對話框中選擇“Class Library”并將工程命名為“Implementation”。
③重復(fù)以上幾步,創(chuàng)建名為“Interfaces”和“Creators”的Class Library工程。展開Interfaces工程節(jié)點,用鼠標(biāo)右鍵單擊Class1.cs,選擇“Rename”命令,將該文件重命名為“IDataRetriever.cs”,并在彈出的對話框中選擇“Yes”。文檔編輯窗口和Solution Explorer如圖5所示。

圖5 文檔編輯窗口和Solution Explorer
④把class的關(guān)鍵字改為interface,將IDataRetriever變成一個接口。為IDataRetriever添加一個get屬性,該屬性返回一個IData類型的對象。在IData下面有紅色波浪線,表示IData不存在。Visual Studio 2010的新功能可以自動解決這個問題:用鼠標(biāo)右鍵單擊出錯的IData,選擇“Generate”命令,然后選擇“Other…”命令,將看到一個“New Type”對話框。將其中的“Access:”修改為“public”,將“Kind:”修改為“interface”,其他的保留默認(rèn)設(shè)置,單擊“OK”按鈕。VS會自動向Interfaces工程添加一個IData.cs文件,并在文件中創(chuàng)建一個名為IData的接口。
⑤展開Implementation工程節(jié)點,用鼠標(biāo)右鍵單擊References節(jié)點,選擇“Add Reference…”命令,在彈出的對話框中選擇Projects頁,然后選擇Interfaces工程,單擊“OK”按鈕。
⑥將Class1.cs重命名為DataRetriever.cs。打開DataRetriever.cs文件,修改DataRetriever類使其實現(xiàn)IDataRetriever接口。當(dāng)輸入IDataRetriever的時候沒有出現(xiàn)智能輸入支持,可以手動輸入IDataRetriever,然后會發(fā)現(xiàn)IDataRetriever下面又出現(xiàn)了紅色波浪線。將鼠標(biāo)移動到IDataRetriever上,會注意到在這個單詞開始的位置下方有一個方形的小圖標(biāo)。單擊它并選擇“using Interfaces;”命令,它會自動為你添加所需的using語句,如圖6所示。

圖6 自動化提示添加代碼語句(名字空間)
現(xiàn)在“using Interfaces”已經(jīng)自動添加好了。再次選中這個圖標(biāo),不過這次選擇“Implement interface ‘IDataRetriever’”命令,可自動生成“DataRetriever”代碼文件。如上所示,創(chuàng)建了一個對象,調(diào)用了對象的一個屬性,然后拋出一個“NotImplementedException”異常,程序描述了一個實際系統(tǒng)中經(jīng)常遇到的問題。
⑧接下來向Client工程中添加到Implementation和Interfaces工程的引用。打開Client工程中的Program.cs文件,參考代碼如下:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Implementation;
- using Interfaces;
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
- DataRetriever dr = new DataRetriever();
- IData data = dr.Data;
- }
- }
- }
在這段代碼中,Client工程直接訪問了一個接口(IDataRetriever)的實例(DataRetriever)。在沒有需求功能擴(kuò)展前沒有太大問題,因為所有的數(shù)據(jù)是從DataRetriever中獲?。梢韵胂驞ataRetriever是從SQL數(shù)據(jù)庫中獲取的數(shù)據(jù))。如果將來需要從另一種數(shù)據(jù)源中獲取數(shù)據(jù),在不改動應(yīng)用程序其他部分的情況下實現(xiàn)需求,可以使用Layer Diagram和Layer Validation來保證開發(fā)代碼不會違反這一設(shè)計。
我們可以不對接口的具體實現(xiàn)做任何設(shè)置,而僅僅依賴于接口本身。這是一個相當(dāng)普遍的設(shè)計模式,但是在現(xiàn)實應(yīng)用中很容易被違反。只要一行錯誤的代碼就會破壞這個模式,從而在建立模塊間出現(xiàn)了不必要的依賴關(guān)系(通常使用控制反轉(zhuǎn)(IoC)來解決這個問題)。
⑨創(chuàng)建Layer Diagram??梢詣?chuàng)建一個Layer Diagram來可視化地描述在架構(gòu)中想要維護(hù)的約束關(guān)系。單擊主菜單的Architecture|New Diagram命令,選擇“Layer Diagram”命令,并將層圖命名為“FirstLayerDiagram.layerdiagram”,在彈出的對話框中,將工程命名為“FirstModelingProject”。
⑩塑模范本,將代碼映射到層上。在Layer Diagram Designer中,從工具箱中拖曳出三個Layer工具到設(shè)計平面上,分別由上至下指定層的名稱為Client、Interface、Implementation,代表應(yīng)用程序、工作接口和方法。表示的是Client Logic層依賴于Interfaces層,Implementation層同樣依賴于Interfaces層。但是Client Logic層和Implementation層之間沒有依賴關(guān)系,如圖7所示。

圖7 創(chuàng)建映射
如上圖所示,然后建立各個層次之間的相互關(guān)系。從工具箱中選擇Dependency工具,在Solution Explorer中,選中Client工程并將它拖曳到Layer Diagram上的Client Logic層上,代表Client層會依賴Interface層。這時出現(xiàn)了一個由Client指向Interface的箭頭鏈接。將Interfaces工程拖到Interfaces層上;***,將Implementation工程拖到Implementation層上,代表Implementation層會依賴Interface層;在層右上角的數(shù)字“1”表示該層已經(jīng)和一個工程相關(guān)聯(lián)。
如果選中Client Logic、Interfaces和Implementation層,再打開Layer Explorer,就可以看到和當(dāng)前層關(guān)聯(lián)的項目,這里是Client.exe、Interfaces.dll和Implementation.dll,然后就可以用這張圖來對代碼進(jìn)行約束與驗證,如圖8所示。

圖8 進(jìn)行架構(gòu)驗證
如上圖所示,進(jìn)行架構(gòu)驗證。用鼠標(biāo)右鍵單擊Layer Diagram的任何位置,選擇“Validate Architecture”命令,進(jìn)行驗證。
驗證架構(gòu)(Validate Architecture):可以檢查出我們的程序是否破壞了層次圖中的依賴關(guān)系,如果我們的程序中的CaryLayer項目中的程序調(diào)用了Common項目中的類等于就違反了以前設(shè)計好的層次圖,在驗證架構(gòu)的時候就會失敗。
依賴關(guān)系(Generate Dependencies):可以根據(jù)我們程序中的調(diào)用關(guān)系生成層的依賴關(guān)系。
錯誤列表。命令執(zhí)行完成后會看到“Error List”窗口中有三條錯誤信息,同時指示錯誤發(fā)生的區(qū)域。檢視一下錯誤內(nèi)容,會發(fā)現(xiàn)我們要求的層次依賴關(guān)系被破壞了。這是因為Client工程中的Program.cs直接使用了Implementation工程中定義的類型。而在剛才創(chuàng)建的圖中,這種依賴關(guān)系是錯誤的,如圖3錯誤列表提示。
修正代碼,解決錯誤問題。
打開Program.cs文件,需要確保只使用Interfaces工程中定義的類型,而不能直接使用Implementation工程中定義的類型。我們需要在不產(chǎn)生直接依賴關(guān)系的情況下創(chuàng)建實現(xiàn)IDataRetriever接口的對象。
解決方法是使用Factory模式,利用Factory建立以接口為主的方法。當(dāng)以后要傳回的信息接收器是針對不同的信息來源進(jìn)行處理的,只要調(diào)整Factory方法傳回對應(yīng)的接收器即可,原本的應(yīng)用程序不用改動,因為它都是通過接口決定作業(yè)的,只要實做了同樣接口的類都可以套用,從而增加了程序的彈性和維護(hù)能力。使用工廠(Factory)模式來解決這個問題的步驟如下:
在Solution Explorer中展開Creators工程,將Class1.cs重命名為TypeCreator.cs。
向Creators工程中添加對Implementation和“Interfaces”工程的引用(Creators工程現(xiàn)在依賴于Implementation和Interfaces工程)。
打開TypeCreators.cs,向其中添加一個靜態(tài)方法,該方法返回一個IDataRetriever的對象。
代碼參考如下所示:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Implementation;
- using Interfaces;
- namespace Creators
- {
- public class TypeCreator
- {
- public static IDataRetriever CreateDataRetriever()
- {
- return new DataRetriever();
- }
- }
- }
在Client工程中,移除對Implementation工程的引用,添加對Creators工程的引用。
修改Program.cs,使用剛才新加的方法來創(chuàng)建對象。代碼參考如下所示:
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using Interfaces;
- using Creators;
- namespace Client
- {
- class Program
- {
- static void Main(string[] args)
- {
- IDataRetriever dr = new TypeCreator.CreateDataRetriever();
- IData data = dr.Data;
- }
- }
- }
修正代碼,解決錯誤問題。
重新編譯Solution,并重新打開FirstLayerDiagram,用鼠標(biāo)右鍵單擊,在菜單中執(zhí)行“Validate Architecture”命令。這樣我們就不是直接通過實做的類進(jìn)行信息的存取,而是經(jīng)由Factory取得符合接口定義的內(nèi)容。再做一次層驗證,我們會看到所有的錯誤都消失了。
總結(jié): 通過使用Visual Studio 2010層關(guān)系設(shè)計架構(gòu),我們就可以在開始階段通過層關(guān)系圖來進(jìn)行邏輯設(shè)計,并努力執(zhí)行設(shè)計方案,保證開發(fā)階段與設(shè)計不偏離,通過自動化(例如門控簽入)進(jìn)行強(qiáng)制執(zhí)行,使團(tuán)隊人員的代碼不漂移出架構(gòu),從而避免“漂移”發(fā)生。另外,采用Layer Diagram來驗證代碼架構(gòu)的方法,大型項目也可以通過相同的方式進(jìn)行驗證。這包括如何將代碼映射到層上,以及如何通過手動的方式來驗證代碼是否遵守定義的約束關(guān)系,也可以在編譯代碼的過程中自動地進(jìn)行驗證。
【編輯推薦】


















