寫出高質(zhì)量軟件是困難和復(fù)雜的:不僅僅是為了滿足需求,還應(yīng)該是健壯的,可維護(hù)的,可測試的,并且足夠靈活以適應(yīng)成長和變化。這就是洋蔥架構(gòu)出現(xiàn)的原因,它代表一組優(yōu)秀的開發(fā)實踐,用來開發(fā)任何的軟件應(yīng)用都是一個不錯的方式。

洋蔥架構(gòu),也成為整潔架構(gòu)(The Clean Architecture),用來構(gòu)建具有如下特點的系統(tǒng):

1.    獨立的Frameworks
2.    可測試
3.    獨立的UI
4.    獨立的數(shù)據(jù)庫
5.    獨立的任意外部服務(wù)(代理)

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

看到這張圖,你應(yīng)該能理解為什么稱其為洋蔥架構(gòu)了:D, 沒錯,這就是它的原理圖。注意,并不是只能使用4個圓環(huán),重點在于這里的依賴原則:代碼依賴是從外向內(nèi)的,內(nèi)環(huán)中的代碼不應(yīng)該知道外環(huán)中的任何東西。

這里有一些相關(guān)的詞匯可以幫助更好的理解和熟悉這種方式:

  Entities:應(yīng)用的業(yè)務(wù)對象。

  Use Casess: Use Casess協(xié)調(diào)(Orchestrate)數(shù)據(jù)從Entities的流入和流出,也被稱為Interactors。

  Interface Adapters:這個Adapter集為Use Casess和Entities把數(shù)據(jù)轉(zhuǎn)換為方便使用的格式(如渲染展示在頁面上),Presenters和Controllers屬于這里。

  Frameworks and Drivers:這是實現(xiàn)所有細(xì)節(jié)的地方:UI,Tools,F(xiàn)rameworks等。

 

下面用一張更生動圖來輔助說明它的原理:

Android培訓(xùn),安卓培訓(xùn),手機(jī)開發(fā)培訓(xùn),移動開發(fā)培訓(xùn),云培訓(xùn)培訓(xùn)

依賴原則(The dependency rules)

上面的同心圓代表軟件的不同部分??偟脕碚f,越往里面,代碼級別越高。外層的圓是(實現(xiàn))機(jī)制,而內(nèi)層的圓是原則(Policies)。

讓這個架構(gòu)起作用的最主要原則是依賴原則。這個原則要求源碼依賴只能指向內(nèi)部。內(nèi)部的圓不能知道外圓的任何事情。一般來說,外圓的聲明(包括方法、類、變量或任何軟件實體)不能被內(nèi)圓引用。

同樣的,外圓使用的數(shù)據(jù)格式不能被內(nèi)圓使用,尤其是外圓中的Framework產(chǎn)生的格式。我們不想讓外圓的任何東西影響內(nèi)圓。

 

越往里面抽象級別越高,最外層的圓是低級別的具體細(xì)節(jié)。越往里面內(nèi)容越抽象,并且封裝更高級別的原則(Policies)。最里面的圓是最通用的。

Entities

Entities封裝了企業(yè)級的業(yè)務(wù)規(guī)則。一個Entity可以是一個帶方法的對象,也可以是一個數(shù)據(jù)結(jié)構(gòu)和方法集。Entities可以被用于企業(yè)的其他應(yīng)用。

如果你沒有加入企業(yè),而是僅僅在寫一個簡單的應(yīng)用,那么這些Entities就是這個應(yīng)用的業(yè)務(wù)對象。它們封裝了最通用、最上層的原則。它們是最不容易改變的,即使外部的東西改變了。例如,你不想讓這些對象受到頁面導(dǎo)航、安全的影響。應(yīng)用的任何操作變化都不應(yīng)該影響Entities Layer。

Use Casess

這一層包含了應(yīng)用特有的業(yè)務(wù)規(guī)則。它封裝和實現(xiàn)了系統(tǒng)的所有用例。這些用例協(xié)調(diào)數(shù)據(jù)從entities的流入和流出,并且指導(dǎo)entities使用它們的企業(yè)級業(yè)務(wù)規(guī)則來達(dá)到用例的目標(biāo)。

我們不希望這一層的改變影響到Entities,同時也不希望這一層被外層的改變影響,如外層的數(shù)據(jù)庫,UI或者任何Frameworks的改變,這一層獨立于這些關(guān)注點。

當(dāng)然,我們確實期望應(yīng)用的操作變化影響用例層。如果一個用例的細(xì)節(jié)改變,那么這一層的部分代碼確實會受到影響。

Interface Adapters

這一層包含一個adapters set(數(shù)據(jù)適配器集),它們把適用于Use Casess和entities的數(shù)據(jù)轉(zhuǎn)換為適用于外部服務(wù)(external agency,如Database或Web)的格式。 例如,這一層可以完全包括GUI的MVX架構(gòu),Presenters, Views和Controllers都屬于這里。Models可能僅僅是從Controllers傳到Use Casess的數(shù)據(jù)結(jié)構(gòu),然后從Use Casess返回給Presenters和Views。

 

這一層的數(shù)據(jù)會被轉(zhuǎn)換,從適用于entities和Use Casess的格式轉(zhuǎn)換到適用于所使用的持久化框架的格式(如數(shù)據(jù)庫)。這個圓以內(nèi)的代碼不應(yīng)該知道關(guān)于數(shù)據(jù)庫的任何東西。如果是一個SQL數(shù)據(jù)庫,那么所有的SQL應(yīng)該被限制到這一層,并且通常來說是被限制到層中跟數(shù)據(jù)庫有關(guān)的部分。

 

同樣,這一層也需要一些其他必要的Adapter來把外部的數(shù)據(jù)格式(如來自于外部服務(wù)的格式),轉(zhuǎn)換為適用于Use Casess和entities的格式。

 

Frameworks and Drivers.

最外面的一層通常由Frameworks和Tools組成,如Database,Web Framework等。一般來說,除了用于和內(nèi)層圓交互的連接代碼,你不會在這一層寫很多代碼。

 

這一層是實現(xiàn)所有細(xì)節(jié)的地方。Web和Database都是需要實現(xiàn)的細(xì)節(jié)。我們把這些東西放在外面以減輕來自于它們的傷害(即減輕對他們的依賴)。

 

跨界

 

在圖的右下角是一個我們應(yīng)該如何跨界的例子。它展示了Controllers、Presenters與下一層的Use Casess的交互。注意控制的流向,它開始于Controller,經(jīng)過Use Casess,最終在Presenter中執(zhí)行。同時也請注意Source Code依賴,它們每一個都指向內(nèi)部的Use Casess。

 

我們通常用依賴倒置原則來解決這個明顯的矛盾。比如,在Java這樣的語言里,我們可以使用接口和繼承關(guān)系在合適的地方讓源碼依賴與控制流反向來跨界。

 

例如,假設(shè)Use Cases需要訪問Presenter,當(dāng)然,不能是直接訪問,不然會違反依賴原則,所以我們讓內(nèi)圓的Use Cases訪問一個接口(如圖中的Use Cases output port),然后外圓的Presenter實現(xiàn)這個接口。

在這個架構(gòu)中,同樣的技術(shù)也被用于跨越其他的邊界。我們利用運行時多態(tài)來創(chuàng)建與控制流相反的SourceCode依賴以滿足依賴原則,無論控制流是如何流向的。

 

通常跨界的數(shù)據(jù)都是簡單的數(shù)據(jù)結(jié)構(gòu)。你可以使用簡單的結(jié)構(gòu)或數(shù)據(jù)傳輸對象(Data Transfer Object)。這個數(shù)據(jù)可以簡單的是方法調(diào)用的參數(shù),你也可以把它包裝到一個HashMap或者一個對象。最重要的是獨立的、簡單的數(shù)據(jù)結(jié)構(gòu)才能跨越邊界。不要投機(jī)取巧,如傳輸Entites或者Database rows。我們不想讓這個數(shù)據(jù)結(jié)構(gòu)有任何違反依賴原則的依賴。

 

例如,很多的數(shù)據(jù)庫框架對于query返回一個方便的數(shù)據(jù)格式,我們可以稱之為Row Structure,我們不想向內(nèi)部傳遞這個row structure。這會讓內(nèi)圓知道外圓的內(nèi)容而違反了依賴原則。

 

所以,我們應(yīng)該以最適用于內(nèi)圓使用的格式來傳遞跨界的數(shù)據(jù)。

 

總結(jié)

滿足這些簡單的原則并不難,并且會減少項目進(jìn)程中很多頭疼的問題。通過把軟件分成幾層,并且滿足依賴原則,你將會創(chuàng)建一個本身就可測試的系統(tǒng),同時還有其他的好處。當(dāng)系統(tǒng)的任何外層部分(如Database,Web 框架)廢棄的時候,你可以輕松的替換這些廢棄的元素。

代碼實踐可以參考:https://github.com/android10/Android-CleanArchitecture/releases

本文大部分內(nèi)容譯自The-Clean-Architecture,其中加入了自己學(xué)習(xí)該架構(gòu)時的理解,如有意見和建議,歡迎交流!

http://www.cnblogs.com/oxgen/p/7171165.html