柚子快報(bào)激活碼778899分享:架構(gòu)簡潔之道-5.整潔架構(gòu)
柚子快報(bào)激活碼778899分享:架構(gòu)簡潔之道-5.整潔架構(gòu)
圖中的同心圓分別代表了軟件系統(tǒng)中的不同層次,通常越靠近中心,其所在的軟件層次就越高?;旧?,外層圓代表的是機(jī)制,內(nèi)層圓代表的是策略。當(dāng)然這其中有一條貫穿整個架構(gòu)設(shè)計(jì)的規(guī)則。
即它的依賴關(guān)系規(guī)則:源碼中的依賴關(guān)系必須只指向同心圓的內(nèi)層,由低層機(jī)制指向高層策略。換句話說, 就是任何屬于內(nèi)層圓中的代碼都不應(yīng)該牽涉外層圓中的代碼,尤其是內(nèi)層圓中的代碼不應(yīng)該引用外層圓中代碼所聲明的名字,包括函數(shù)、類、變量以及一切其他有命名的軟件實(shí)體。
同樣的道理,外層圓中使用的數(shù)據(jù)格式也不應(yīng)該被內(nèi)層圓中的代碼所使用,尤其是當(dāng)數(shù)據(jù)格式是由外層圓的框架所生成時。總之,我們不應(yīng)該讓外層圓中發(fā)生的任何變更影響到內(nèi)層圓的代碼。業(yè)務(wù)實(shí)體
業(yè)務(wù)實(shí)體這一層中封裝的是整個系統(tǒng)的關(guān)鍵業(yè)務(wù)邏輯, 一個業(yè)務(wù)實(shí)體既可以是一個帶有方法的對象,也可以是一組數(shù)據(jù)結(jié)構(gòu)和函數(shù)的集合。無論如何,只要它能被系統(tǒng)中的其他不同應(yīng)用復(fù)用就可以。如果我們在寫的不是一個大型系統(tǒng),而是一個單一應(yīng)用的話,那么我們的業(yè)務(wù)實(shí)體就是該應(yīng)用的業(yè)務(wù)對象。這些對象封裝了該應(yīng)用中最通用、最高層的業(yè)務(wù)邏輯,它們應(yīng)該屬于系統(tǒng)中最不容易受外界影響而變動的部分。例如,一個針對頁面導(dǎo)航方式或者安全問題的修改不應(yīng)該觸及這些對象, 一個針對應(yīng)用在運(yùn)行時的行為所做的變更也不應(yīng)該影響業(yè)務(wù)實(shí)體。用例
軟件的用例層中通常包含的是特定應(yīng)用場景下的業(yè)務(wù)邏輯,這里面封裝并實(shí)現(xiàn)了整個系統(tǒng)的所有用例。這些用例引導(dǎo)了數(shù)據(jù)在業(yè)務(wù)實(shí)體之間的流入/流出,并指揮著業(yè)務(wù)實(shí)體利用其中的關(guān)鍵業(yè)務(wù)邏輯來實(shí)現(xiàn)用例的設(shè)計(jì)目標(biāo)。我們既不希望在這一層所發(fā)生的變更影響業(yè)務(wù)實(shí)體,同時也不希望這一層受外部因素(譬如數(shù)據(jù)庫、UI 、常見框架)的影響。用例層應(yīng)該與它們都保持隔離。然而,我們知道應(yīng)用行為的變化會影響用例本身,因此一定會影響用例層的碼。因?yàn)槿绻粋€用例的細(xì)節(jié)發(fā)生了變化,這一層中的某些代碼自然要受到影響。接口適配器
軟件的接口適配器層中通常是一組數(shù)據(jù)轉(zhuǎn)換器,它們負(fù)責(zé)將數(shù)據(jù)從對用例和業(yè)務(wù)實(shí)體而言最方便操作的格式,轉(zhuǎn)化成外部系統(tǒng)(譬如數(shù)據(jù)庫以及Web )最方便操作的格式。例如,這一層中應(yīng)該包含整個GUI MVC 框架。展示器、視圖、控制器都應(yīng)該屬于接口適配器層。而模型部分則應(yīng)該由控制器傳遞給用例,再由用例傳回展示器和視圖。同樣的,這一層的代碼也會負(fù)責(zé)將數(shù)據(jù)從對業(yè)務(wù)實(shí)體與用例而言最方便操作的格式,轉(zhuǎn)化為對所采用的持久性框架(譬如數(shù)據(jù)庫〉最方便的格式??傊趶脑搶釉偻鶅?nèi)的同心圓中, 其代碼就不應(yīng)該依賴任何數(shù)據(jù)庫了。譬如說,如果我們采用的是SQL 數(shù)據(jù)庫,那么所有的SQL 語句都應(yīng)該被限制在這一層的代碼中一一而且是僅限于那些需要操作數(shù)據(jù)庫的代碼。當(dāng)然,這一層的代碼也會負(fù)責(zé)將來自外部服務(wù)的數(shù)據(jù)轉(zhuǎn)換成系統(tǒng)內(nèi)用例與業(yè)務(wù)實(shí)體所需的格式。框架與驅(qū)動程序
圖中最外層的模型層一般是由工具、數(shù)據(jù)庫、Web 框架等組成的。在這一層中,我們通常只需要編寫一些與內(nèi)層溝通的勤合性代碼。 框架與驅(qū)動程序?qū)又邪怂械膶?shí)現(xiàn)細(xì)節(jié)。Web 是一個實(shí)現(xiàn)細(xì)節(jié),數(shù)據(jù)庫也是一個實(shí)現(xiàn)細(xì)節(jié)。我們將這些細(xì)節(jié)放在最外層,這樣它們就很難影響到其他層了。只有四層嗎圖中所顯示的同心圓只是為了說明架構(gòu)的結(jié)構(gòu),真正的架構(gòu)很可能會超過四層。并沒有某個規(guī)則約定一個系統(tǒng)的架構(gòu)有且只能有四層。然而,這其中的依賴關(guān)系原則是不變的。也就是說,源碼層面的依賴關(guān)系一定要指向同心圓的內(nèi)側(cè)。層次越往內(nèi),其抽象和策略的層次越高, 同時軟件的抽象程度就越高其包含的高層策略就越多。最內(nèi)層的圓中包含的是最通用、最高層的策略,最外層的圓包含的是最具體的實(shí)現(xiàn)細(xì)節(jié)??缭竭吔?在圖的右下側(cè),我們示范的是在架構(gòu)中跨邊界的情況。具體來說就是控器、展示器與下一層的用例之間的通信過程。請注意這里控制流的方向:它從控制器開始,穿過用例,最后執(zhí)行展示器的代碼。但同時我們也該注意到,源碼中的依賴方向卻都是向內(nèi)指向用例的。 這里,我們通常采用依賴反轉(zhuǎn)原則( DI 歸來解決這種相反性。例如,在Java這一類的語言中,可以通過調(diào)整代碼中的接口和繼承關(guān)系,利用源碼中的依賴關(guān)系來限制控制流只能在正確的地方跨越架構(gòu)邊界。假設(shè)某些用例代碼需要調(diào)用展示器,這里一定不能直接調(diào)用,因?yàn)檫@樣做會違反依賴關(guān)系原則: 內(nèi)層圓中的代碼不能引用其外層的聲明。我們需要讓業(yè)務(wù)邏輯代碼調(diào)用一個內(nèi)層接口(圖中的“用例輸出端”〉,并讓展示器來負(fù)責(zé)實(shí)現(xiàn)這個接口。我們可以采用這種方式跨越系統(tǒng)中所有的架構(gòu)邊界。利用動態(tài)多態(tài)技術(shù),我們將源碼中的依賴關(guān)系與控制流的方向進(jìn)行反轉(zhuǎn)。不管控制流原本的方向如何,我們 都可以讓它遵守架構(gòu)的依賴關(guān)系規(guī)則。哪些數(shù)據(jù)會跨越邊界 一般來說,會跨越邊界的數(shù)據(jù)在數(shù)據(jù)結(jié)構(gòu)上都是很簡單的。如果可以的話,我們會盡量采用一些基本的結(jié)構(gòu)體或簡單的可傳輸數(shù)據(jù)對象。或者直接通過函數(shù)調(diào)用的參數(shù)來傳遞數(shù)據(jù)。另外, 我們也可以將數(shù)據(jù)放入哈希表,或整合成某種對象。這里最重要的是這個跨邊界傳輸?shù)膶ο髴?yīng)該有一個獨(dú)立、簡單的數(shù)據(jù)結(jié)構(gòu)??傊?,不要投機(jī)取巧地直接傳遞業(yè)務(wù)實(shí)體或數(shù)據(jù)庫記錄對象。同時,這些傳遞的數(shù)據(jù)結(jié)構(gòu)中 也不應(yīng)該存在違反依賴規(guī)則的依賴關(guān)系。例如,很多數(shù)據(jù)庫框架會返回一個便于查詢的結(jié)果對象,我們稱之為“行結(jié)構(gòu)體”。這個結(jié)構(gòu)體不應(yīng)該跨邊界向架構(gòu)的內(nèi)層傳遞。因?yàn)檫@等于讓內(nèi)層的代碼引用 外層代碼,違反依賴規(guī)則。因此,當(dāng)我們進(jìn)行跨邊界傳輸時, 一定要采用內(nèi)層最方便使用的形式。
柚子快報(bào)激活碼778899分享:架構(gòu)簡潔之道-5.整潔架構(gòu)
精彩文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。