Visual Studio 2010層架構(gòu)驗(yàn)證的實(shí)現(xiàn)
原創(chuàng)【51CTO獨(dú)家特稿】當(dāng)設(shè)計(jì)一個(gè)應(yīng)用程序時(shí),基本的要求是所有的通信信息交互都要通過(guò)定義的接口進(jìn)行,不能讓?xiě)?yīng)用程序直接調(diào)用該接口的類別或方法。所以,可以通過(guò)Layer Diagram來(lái)展示這個(gè)架構(gòu)上的想法。51CTO向您推薦《用好Visual Studio 2010進(jìn)行層架構(gòu)設(shè)計(jì)》
我們將使用一段非常簡(jiǎn)單的代碼,主要強(qiáng)調(diào)的是代碼所代表的概念,而不是代碼的細(xì)節(jié)。并將在現(xiàn)有代碼層關(guān)系架構(gòu)邏輯設(shè)計(jì)分析的基礎(chǔ)上進(jìn)行層驗(yàn)證(Layer Validation)功能:
①打開(kāi)Visual Studio 2010,通過(guò)Modeling Projects模板指定解決方案(Solution)的名稱為L(zhǎng)ayerValidation(File|New|Project命令,選擇ModelingProject,命名為L(zhǎng)ayerValidation),并提供創(chuàng)建一個(gè)名為“Client”的C#控制臺(tái)程序,單擊“OK”按鈕。
②在解決方案瀏覽器中,使用鼠標(biāo)右鍵單擊Solution節(jié)點(diǎn),選擇“New Project…”命令,在彈出的對(duì)話框中選擇“Class Library”并將工程命名為“Implementation”。
③重復(fù)以上幾步,創(chuàng)建名為“Interfaces”和“Creators”的Class Library工程。展開(kāi)Interfaces工程節(jié)點(diǎn),用鼠標(biāo)右鍵單擊Class1.cs,選擇“Rename”命令,將該文件重命名為“IDataRetriever.cs”,并在彈出的對(duì)話框中選擇“Yes”。文檔編輯窗口和Solution Explorer如圖5所示。
圖5 文檔編輯窗口和Solution Explorer
④把class的關(guān)鍵字改為interface,將IDataRetriever變成一個(gè)接口。為IDataRetriever添加一個(gè)get屬性,該屬性返回一個(gè)IData類型的對(duì)象。在IData下面有紅色波浪線,表示IData不存在。Visual Studio 2010的新功能可以自動(dòng)解決這個(gè)問(wèn)題:用鼠標(biāo)右鍵單擊出錯(cuò)的IData,選擇“Generate”命令,然后選擇“Other…”命令,將看到一個(gè)“New Type”對(duì)話框。將其中的“Access:”修改為“public”,將“Kind:”修改為“interface”,其他的保留默認(rèn)設(shè)置,單擊“OK”按鈕。VS會(huì)自動(dòng)向Interfaces工程添加一個(gè)IData.cs文件,并在文件中創(chuàng)建一個(gè)名為IData的接口。
⑤展開(kāi)Implementation工程節(jié)點(diǎn),用鼠標(biāo)右鍵單擊References節(jié)點(diǎn),選擇“Add Reference…”命令,在彈出的對(duì)話框中選擇Projects頁(yè),然后選擇Interfaces工程,單擊“OK”按鈕。
⑥將Class1.cs重命名為DataRetriever.cs。打開(kāi)DataRetriever.cs文件,修改DataRetriever類使其實(shí)現(xiàn)IDataRetriever接口。當(dāng)輸入IDataRetriever的時(shí)候沒(méi)有出現(xiàn)智能輸入支持,可以手動(dòng)輸入IDataRetriever,然后會(huì)發(fā)現(xiàn)IDataRetriever下面又出現(xiàn)了紅色波浪線。將鼠標(biāo)移動(dòng)到IDataRetriever上,會(huì)注意到在這個(gè)單詞開(kāi)始的位置下方有一個(gè)方形的小圖標(biāo)。單擊它并選擇“using Interfaces;”命令,它會(huì)自動(dòng)為你添加所需的using語(yǔ)句,如圖6所示。
圖6 自動(dòng)化提示添加代碼語(yǔ)句(名字空間)
現(xiàn)在“using Interfaces”已經(jīng)自動(dòng)添加好了。再次選中這個(gè)圖標(biāo),不過(guò)這次選擇“Implement interface ‘IDataRetriever’”命令,可自動(dòng)生成“DataRetriever”代碼文件。如上所示,創(chuàng)建了一個(gè)對(duì)象,調(diào)用了對(duì)象的一個(gè)屬性,然后拋出一個(gè)“NotImplementedException”異常,程序描述了一個(gè)實(shí)際系統(tǒng)中經(jīng)常遇到的問(wèn)題。
⑧接下來(lái)向Client工程中添加到Implementation和Interfaces工程的引用。打開(kāi)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工程直接訪問(wèn)了一個(gè)接口(IDataRetriever)的實(shí)例(DataRetriever)。在沒(méi)有需求功能擴(kuò)展前沒(méi)有太大問(wèn)題,因?yàn)樗械臄?shù)據(jù)是從DataRetriever中獲取(可以想象DataRetriever是從SQL數(shù)據(jù)庫(kù)中獲取的數(shù)據(jù))。如果將來(lái)需要從另一種數(shù)據(jù)源中獲取數(shù)據(jù),在不改動(dòng)應(yīng)用程序其他部分的情況下實(shí)現(xiàn)需求,可以使用Layer Diagram和Layer Validation來(lái)保證開(kāi)發(fā)代碼不會(huì)違反這一設(shè)計(jì)。
我們可以不對(duì)接口的具體實(shí)現(xiàn)做任何設(shè)置,而僅僅依賴于接口本身。這是一個(gè)相當(dāng)普遍的設(shè)計(jì)模式,但是在現(xiàn)實(shí)應(yīng)用中很容易被違反。只要一行錯(cuò)誤的代碼就會(huì)破壞這個(gè)模式,從而在建立模塊間出現(xiàn)了不必要的依賴關(guān)系(通常使用控制反轉(zhuǎn)(IoC)來(lái)解決這個(gè)問(wèn)題)。
⑨創(chuàng)建Layer Diagram。可以創(chuàng)建一個(gè)Layer Diagram來(lái)可視化地描述在架構(gòu)中想要維護(hù)的約束關(guān)系。單擊主菜單的Architecture|New Diagram命令,選擇“Layer Diagram”命令,并將層圖命名為“FirstLayerDiagram.layerdiagram”,在彈出的對(duì)話框中,將工程命名為“FirstModelingProject”。
⑩塑模范本,將代碼映射到層上。在Layer Diagram Designer中,從工具箱中拖曳出三個(gè)Layer工具到設(shè)計(jì)平面上,分別由上至下指定層的名稱為Client、Interface、Implementation,代表應(yīng)用程序、工作接口和方法。表示的是Client Logic層依賴于Interfaces層,Implementation層同樣依賴于Interfaces層。但是Client Logic層和Implementation層之間沒(méi)有依賴關(guān)系,如圖7所示。
圖7 創(chuàng)建映射
如上圖所示,然后建立各個(gè)層次之間的相互關(guān)系。從工具箱中選擇Dependency工具,在Solution Explorer中,選中Client工程并將它拖曳到Layer Diagram上的Client Logic層上,代表Client層會(huì)依賴Interface層。這時(shí)出現(xiàn)了一個(gè)由Client指向Interface的箭頭鏈接。將Interfaces工程拖到Interfaces層上;***,將Implementation工程拖到Implementation層上,代表Implementation層會(huì)依賴Interface層;在層右上角的數(shù)字“1”表示該層已經(jīng)和一個(gè)工程相關(guān)聯(lián)。
如果選中Client Logic、Interfaces和Implementation層,再打開(kāi)Layer Explorer,就可以看到和當(dāng)前層關(guān)聯(lián)的項(xiàng)目,這里是Client.exe、Interfaces.dll和Implementation.dll,然后就可以用這張圖來(lái)對(duì)代碼進(jìn)行約束與驗(yàn)證,如圖8所示。
圖8 進(jìn)行架構(gòu)驗(yàn)證
如上圖所示,進(jìn)行架構(gòu)驗(yàn)證。用鼠標(biāo)右鍵單擊Layer Diagram的任何位置,選擇“Validate Architecture”命令,進(jìn)行驗(yàn)證。
驗(yàn)證架構(gòu)(Validate Architecture):可以檢查出我們的程序是否破壞了層次圖中的依賴關(guān)系,如果我們的程序中的CaryLayer項(xiàng)目中的程序調(diào)用了Common項(xiàng)目中的類等于就違反了以前設(shè)計(jì)好的層次圖,在驗(yàn)證架構(gòu)的時(shí)候就會(huì)失敗。
依賴關(guān)系(Generate Dependencies):可以根據(jù)我們程序中的調(diào)用關(guān)系生成層的依賴關(guān)系。
錯(cuò)誤列表。命令執(zhí)行完成后會(huì)看到“Error List”窗口中有三條錯(cuò)誤信息,同時(shí)指示錯(cuò)誤發(fā)生的區(qū)域。檢視一下錯(cuò)誤內(nèi)容,會(huì)發(fā)現(xiàn)我們要求的層次依賴關(guān)系被破壞了。這是因?yàn)镃lient工程中的Program.cs直接使用了Implementation工程中定義的類型。而在剛才創(chuàng)建的圖中,這種依賴關(guān)系是錯(cuò)誤的,如圖3錯(cuò)誤列表提示。
修正代碼,解決錯(cuò)誤問(wèn)題。
打開(kāi)Program.cs文件,需要確保只使用Interfaces工程中定義的類型,而不能直接使用Implementation工程中定義的類型。我們需要在不產(chǎn)生直接依賴關(guān)系的情況下創(chuàng)建實(shí)現(xiàn)IDataRetriever接口的對(duì)象。
解決方法是使用Factory模式,利用Factory建立以接口為主的方法。當(dāng)以后要傳回的信息接收器是針對(duì)不同的信息來(lái)源進(jìn)行處理的,只要調(diào)整Factory方法傳回對(duì)應(yīng)的接收器即可,原本的應(yīng)用程序不用改動(dòng),因?yàn)樗际峭ㄟ^(guò)接口決定作業(yè)的,只要實(shí)做了同樣接口的類都可以套用,從而增加了程序的彈性和維護(hù)能力。使用工廠(Factory)模式來(lái)解決這個(gè)問(wèn)題的步驟如下:
在Solution Explorer中展開(kāi)Creators工程,將Class1.cs重命名為T(mén)ypeCreator.cs。
向Creators工程中添加對(duì)Implementation和“Interfaces”工程的引用(Creators工程現(xiàn)在依賴于Implementation和Interfaces工程)。
打開(kāi)TypeCreators.cs,向其中添加一個(gè)靜態(tài)方法,該方法返回一個(gè)IDataRetriever的對(duì)象。
代碼參考如下所示:
- 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工程中,移除對(duì)Implementation工程的引用,添加對(duì)Creators工程的引用。
修改Program.cs,使用剛才新加的方法來(lái)創(chuàng)建對(duì)象。代碼參考如下所示:
- 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;
- }
- }
- }
修正代碼,解決錯(cuò)誤問(wèn)題。
重新編譯Solution,并重新打開(kāi)FirstLayerDiagram,用鼠標(biāo)右鍵單擊,在菜單中執(zhí)行“Validate Architecture”命令。這樣我們就不是直接通過(guò)實(shí)做的類進(jìn)行信息的存取,而是經(jīng)由Factory取得符合接口定義的內(nèi)容。再做一次層驗(yàn)證,我們會(huì)看到所有的錯(cuò)誤都消失了。
總結(jié): 通過(guò)使用Visual Studio 2010層關(guān)系設(shè)計(jì)架構(gòu),我們就可以在開(kāi)始階段通過(guò)層關(guān)系圖來(lái)進(jìn)行邏輯設(shè)計(jì),并努力執(zhí)行設(shè)計(jì)方案,保證開(kāi)發(fā)階段與設(shè)計(jì)不偏離,通過(guò)自動(dòng)化(例如門(mén)控簽入)進(jìn)行強(qiáng)制執(zhí)行,使團(tuán)隊(duì)人員的代碼不漂移出架構(gòu),從而避免“漂移”發(fā)生。另外,采用Layer Diagram來(lái)驗(yàn)證代碼架構(gòu)的方法,大型項(xiàng)目也可以通過(guò)相同的方式進(jìn)行驗(yàn)證。這包括如何將代碼映射到層上,以及如何通過(guò)手動(dòng)的方式來(lái)驗(yàn)證代碼是否遵守定義的約束關(guān)系,也可以在編譯代碼的過(guò)程中自動(dòng)地進(jìn)行驗(yàn)證。
【編輯推薦】