要設(shè)計(jì)良好的架構(gòu),必須做到關(guān)注點(diǎn)分離,這樣可以產(chǎn)生高內(nèi)聚、低耦合的系統(tǒng),這是美麗架構(gòu)的終極原則。
文 / 王海鵬
什么是架構(gòu)? 每個(gè)人可能都有自己對(duì)架構(gòu)的定義。我比較喜歡的定義是:“架構(gòu)是系統(tǒng)的組成部件及其之間的相互關(guān)系。”根據(jù)觀察者的視角不同,架構(gòu)又可以分為業(yè)務(wù)架構(gòu)和技術(shù)架構(gòu)。一般來(lái)說(shuō), 功能性需求會(huì)對(duì)業(yè)務(wù)架構(gòu)產(chǎn)生影響, 而非功能性需求會(huì)對(duì)技術(shù)架構(gòu)產(chǎn)生影響。
例如:“注冊(cè)用戶可以向自己的相冊(cè)上傳圖片,并與好友分享”。這是一項(xiàng)功能性需求。它告訴了我們?cè)谙到y(tǒng)的業(yè)務(wù)架構(gòu)中,會(huì)出現(xiàn)“注冊(cè)用戶”“相冊(cè)”、“圖片”、“好友”等組成部件,它們之間存在著相互關(guān)系。而“系統(tǒng)可以支持10萬(wàn)并發(fā)用戶,并在需要時(shí)可以方便地伸縮,擴(kuò)展到支持100萬(wàn)到1000萬(wàn)的并發(fā)用戶”,則是一項(xiàng)非功能性需求。它告訴了我們系統(tǒng)在性能、負(fù)載、吞吐量、可伸縮性方面的特性,目標(biāo)系統(tǒng)的架構(gòu)必須對(duì)這些特性提供支持。
架構(gòu)體現(xiàn)的是對(duì)復(fù)雜系統(tǒng)的分解設(shè)計(jì)。而如何進(jìn)行分解,則是軟件設(shè)計(jì)領(lǐng)域永恒的話題。實(shí)際上,架構(gòu)體現(xiàn)的是關(guān)注點(diǎn)分離的原則和方法。經(jīng)典的三層架構(gòu),由展現(xiàn)層、業(yè)務(wù)邏輯層和持久層構(gòu)成;其中體現(xiàn)了我們對(duì)用戶界面、業(yè)務(wù)邏輯和數(shù)據(jù)持久的關(guān)注點(diǎn)分離。這種架構(gòu)從命令行時(shí)代的軟件就開始有了,直到最新的AJAX 加RESTful的Web 應(yīng)用架構(gòu)中仍然可以看到它,因?yàn)檫@種關(guān)注點(diǎn)的分離在這些應(yīng)用中是必要的。
我們可以在Web 應(yīng)用中不采用三層架構(gòu),也就是不進(jìn)行這種分離。我們可以在JSP/ASP/PHP中混合用戶界面、業(yè)務(wù)邏輯和數(shù)據(jù)持久層。但是這樣的代碼是難以維護(hù)的,難以適應(yīng)大規(guī)模項(xiàng)目開發(fā),難以適應(yīng)將來(lái)的變化。關(guān)注點(diǎn)不分離的代碼為閱讀和理解制造了更多的障礙。用戶界面、業(yè)務(wù)邏輯和數(shù)據(jù)持久三者的分離,讓它們能夠相對(duì)獨(dú)立地進(jìn)行變化,比如實(shí)現(xiàn)新的用戶界面方式、改變業(yè)務(wù)邏輯和采用新的數(shù)據(jù)持久機(jī)制。
依賴注入的架構(gòu)方式因Spring、Guice 等框架而被廣大Java 程序員所熟悉,進(jìn)而擴(kuò)展到.NET等其他語(yǔ)言和平臺(tái),它也體現(xiàn)了關(guān)注點(diǎn)的分離。
首先,依賴注入體現(xiàn)了“做什么”和“怎么做”的關(guān)注點(diǎn)分離。學(xué)過(guò)C語(yǔ)言的程序員都知道,這種關(guān)注點(diǎn)的分離有著悠久的歷史,它表現(xiàn)為.h文件和.c文件,一個(gè)規(guī)定函數(shù)原型,一個(gè)規(guī)定函數(shù)實(shí)現(xiàn)。這種關(guān)注點(diǎn)分離后來(lái)還在面向?qū)ο蟮脑O(shè)計(jì)中表現(xiàn)為“針對(duì)接口設(shè)計(jì)”,于是我們?cè)谝蕾囎⑷氲募軜?gòu)方式中,看到了許多的接口,以及接口的不同實(shí)現(xiàn)。組件的使用者關(guān)注組件做什么,組件的實(shí)現(xiàn)者關(guān)注組件怎么做。其次,依賴注入體現(xiàn)了對(duì)實(shí)例生命周期(特別是實(shí)例創(chuàng)建)和組件裝配的關(guān)注點(diǎn)分離。我們可以集中指定對(duì)象創(chuàng)建的方式,方便靈活地改變系統(tǒng)的裝配方式。通過(guò)這樣的關(guān)注點(diǎn)分離,依賴注入的架構(gòu)讓系統(tǒng)變得更靈活,讓組件的實(shí)例化和組裝方式集中在系統(tǒng)的一個(gè)局部來(lái)確定,而不是分散在系統(tǒng)各處。
《彩色UML建模》一書中提出了4種領(lǐng)域無(wú)關(guān)的架構(gòu)型,體現(xiàn)了業(yè)務(wù)流程、業(yè)務(wù)事件、參與者角色、具體參與者、分類分組的關(guān)注點(diǎn)分離。它研究的是業(yè)務(wù)架構(gòu),告訴我們?nèi)绾卧O(shè)計(jì)一些組件來(lái)體現(xiàn)業(yè)務(wù)邏輯。
以ATM機(jī)取款為例,它包含一個(gè)業(yè)務(wù)流程,由身份認(rèn)證、取款、打印憑據(jù)等事件組成。單獨(dú)來(lái)看取款這個(gè)事件,彩色UML方法會(huì)在模型中體現(xiàn)出“取款(時(shí)刻/ 時(shí)段)”、“賬戶(物品)”、“信用卡(物品)”、“ATM機(jī)/ 取款柜臺(tái)(地點(diǎn))”等組件。這樣的關(guān)注點(diǎn)分離,使得這一組取款組件可以從業(yè)務(wù)流程中獨(dú)立出來(lái),所以它也可以參與其他業(yè)務(wù)流程,如柜臺(tái)取款。
REST是一種Web應(yīng)用架構(gòu)風(fēng)格,它的主要特點(diǎn)包括:
- 資源是由URI來(lái)指定。
- 對(duì)資源的操作包括獲取、創(chuàng)建、修改和刪除資源,這些操作正好對(duì)應(yīng)HTTP協(xié)議提供的GET、POST、PUT和DELETE方法。
- 通過(guò)操作資源的表形來(lái)操作資源。
- 可以通過(guò)內(nèi)容協(xié)商機(jī)制來(lái)取得同一資源的不同物理表現(xiàn)形式,可以是XML、HTML、JSON或其他格式,這取決于請(qǐng)求者是機(jī)器還是人,是消費(fèi)Web服務(wù)的客戶軟件還是Web瀏覽器。
在REST架構(gòu)風(fēng)格中,體現(xiàn)了對(duì)資源命名、請(qǐng)求處理和資源物理表現(xiàn)形式的關(guān)注點(diǎn)分離。
由于實(shí)現(xiàn)了這些關(guān)注點(diǎn)分離,REST有下面這些好處:
- 可以利用緩存Cache來(lái)提高響應(yīng)速度。
- 通訊本身的無(wú)狀態(tài)性可以讓不同服務(wù)器處理一系列請(qǐng)求中的不同請(qǐng)求,提高服務(wù)器的擴(kuò)展性。
- 瀏覽器即可作為客戶端,簡(jiǎn)化軟件需求。
- 相對(duì)與其他疊加在HTTP協(xié)議之上的機(jī)制,REST的軟件依賴性更小。
- 不需要額外的資源發(fā)現(xiàn)機(jī)制。
- 在軟件技術(shù)演進(jìn)中的長(zhǎng)期的兼容性更好。
AOP(面向方面編程)也是關(guān)注點(diǎn)分離思想的一種體現(xiàn)。記日志是說(shuō)明AOP 思想的一個(gè)常用例子。當(dāng)我們?cè)跇I(yè)務(wù)代碼中嵌入很多日志代碼時(shí),業(yè)務(wù)代碼的可讀性就下降了,因?yàn)闃I(yè)務(wù)邏輯和日志是兩個(gè)不同的關(guān)注點(diǎn)。通過(guò)AOP技術(shù)來(lái)分離這兩個(gè)關(guān)注點(diǎn),就提高了代碼的可讀性和可維護(hù)性。
Ivar Jacobson在他的《Aspect-Oriented Software Developmentwith Use Case》一書中, 提出了AOP思想與用例技術(shù)結(jié)合的方法。例如,在“用戶利用電話系統(tǒng)通話”和“對(duì)用戶的通話計(jì)費(fèi)”這兩個(gè)用例中,我們的關(guān)注點(diǎn)是不同的。但是如果不采用AOP的方法,實(shí)現(xiàn)代碼中必然將這兩個(gè)關(guān)注點(diǎn)的代碼編織在一起。如果對(duì)一個(gè)業(yè)務(wù)過(guò)程有許多不同的關(guān)注點(diǎn),代碼就變得復(fù)雜而難讀了。于是Ivar提出,分離兩個(gè)關(guān)注點(diǎn), 再利用AOP的技術(shù)將它們最終編織在一起。這樣,我們就能單獨(dú)面對(duì)一個(gè)方面的關(guān)注點(diǎn)。
AOP技術(shù)用一種特別的方式實(shí)現(xiàn)了關(guān)注點(diǎn)分離,它的好處是明顯的,但也有一些不足。比如對(duì)于編織后的系統(tǒng)除錯(cuò),會(huì)因?yàn)閳?zhí)行流的跳轉(zhuǎn)而變得比較復(fù)雜。這時(shí)候,我們需要通過(guò)一些其他技術(shù)來(lái)彌補(bǔ),例如回歸測(cè)試。我們?cè)趯?shí)現(xiàn)了“用戶利用電話系統(tǒng)通話”后,寫一個(gè)回歸測(cè)試將工作成果確定下來(lái)。在我們實(shí)現(xiàn)之后的業(yè)務(wù)關(guān)注點(diǎn)時(shí),經(jīng)常跑這個(gè)回歸測(cè)試,確保不會(huì)因?yàn)樾碌木幙椂谙到y(tǒng)中引入缺陷。
EJB技術(shù)雖然在早期受到了一些詬病,但是它的架構(gòu)設(shè)計(jì)總體上仍然是非常漂亮的。EJB體現(xiàn)了分布式計(jì)算、持久、實(shí)例化、緩沖、事務(wù)、安全性等關(guān)注點(diǎn)的分離。
Google提出的MapReduce技術(shù)是一種新的集群計(jì)算思想, 它體現(xiàn)了分布式并行計(jì)算的關(guān)注點(diǎn)分離。Apache 的Hadoop項(xiàng)目就是這種技術(shù)的一個(gè)實(shí)現(xiàn)。通過(guò)這種技術(shù),實(shí)現(xiàn)了對(duì)大規(guī)模分布式系統(tǒng)的性能、可伸縮性、可靠性的關(guān)注點(diǎn)分離。程序員可以在不了解太多分布式計(jì)算細(xì)節(jié)的情況下,開發(fā)出大規(guī)模分布計(jì)算程序。
關(guān)注點(diǎn)分離帶來(lái)的是高內(nèi)聚、低耦合的系統(tǒng),這是美麗架構(gòu)的終極原則。在實(shí)踐中,這種關(guān)注點(diǎn)分離可以是一開始就設(shè)計(jì)好的,也可以是逐漸形成的。如果是逐漸形成的,那就是所謂的演進(jìn)式架構(gòu)。例如,我們可以先不實(shí)現(xiàn)持久機(jī)制,在項(xiàng)目進(jìn)行到一定階段再來(lái)實(shí)現(xiàn)。或者在以后方便地更換持久機(jī)制。
我們?cè)谙到y(tǒng)分析時(shí)確定系統(tǒng)都有哪些關(guān)注點(diǎn),然后設(shè)計(jì)一個(gè)架構(gòu)來(lái)支持這些關(guān)注點(diǎn)的分離,最終將得到在相同的關(guān)注點(diǎn)上高內(nèi)聚,在不同的關(guān)注點(diǎn)上低耦合的設(shè)計(jì)。
文章轉(zhuǎn)自:http://www.programmer.com.cn/675/