天天看點

[Android]一個幹淨的架構(翻譯)

以下内容為原創,歡迎轉載,轉載請注明

來自天天部落格:http://www.cnblogs.com/tiantianbyconan/p/5276587.html

一個幹淨的架構

原文:https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

在過去幾年中我們能看到的一系列關于系統架構的思想。它們包括:

  • Hexagonal Architecture(也稱為

    Ports and Adapters

    ),作者是 Alistair Cockburn,并被 Steve Freeman 和 Nat Pryce 在他們很棒的 Growing Object Oriented Software 這本書中采用。
  • Onion Architecture,作者是 Jeffrey Palermo。
  • Screaming Architecture,來自我去年寫的一篇部落格。
  • DCI,來自 James Coplien 和 Trygve Reenskaug。
  • BCE,來自 Ivar Jacobson 的一本書 Object Oriented Software Engineering: A Use-Case Driven Approach

盡管這些書在細節上有些不同之處,但是它們是非常相似的。從分離的觀點上來講它們都有着同樣的目标。它們都是通過軟體分層來實作分離的。每一個都至少有一層是用于業務規則,另外一層用于接口。

這些架構生産系統:

  1. 架構獨立性。架構并不依賴于已經存在的某些庫的有負載的特性。這允許你作為工具去使用架構,而不是把你的系統強塞到限制和限制中。
  2. 可測試性。業務規則可以脫離UI、資料庫、web伺服器和其它外部元素去進行測試。
  3. UI的獨立性。UI可以在不修改系統其它地方的情況下很容易地被改變。比如,一個Web UI可以使用console UI替換,而不改變任何業務規則。
  4. 資料庫獨立。你可以使用Mongo、BitTable、CouchDB或者其它來置換Oracle或SQL Server。你的業務規則不會被資料庫束縛。
  5. 外部代理的獨立性。事實上,你的業務規則根本不知道外面的世界。

這篇文章頂部的圖表就是嘗試所有這些結構到單個可操作的思想中去。

依賴準則

同心圓表示不同軟體的區域。一般情況下,走得越遠,軟體水準也會變得越高。外面的圓是機制,而内部的圓是政策。

使得這個架構可行的最重要的規則是 依賴準則 。這個準則指 代碼的依賴 隻能 向内 。沒有一個内部圓可以知道外部圓的任何東西。尤其是在内部圓聲明的東西名字不能被内部圓中的代碼提到。這裡包括方法、類、變量、或者其它軟體實體的名字。

同樣的原因,外部圓中用到的資料格式不能被内部圓使用,尤其是這些格式是被外部圓中的架構生成的。我們不希望任何在外部圓中的東西影響到内部圓。

Entities

實體封裝了 企業級 業務規則。一個實體可以是一個有方法的對象,或者有一系列的資料結構和函數。隻要這個實體可以被很多不同企業中的應用使用就沒關系。

如果你沒有一個企業,并且隻是編寫單個的應用程式,那麼這些實體就是應用程式中的業務對象。它封裝了大部分一般的和進階别的規則。它們最不可能在外部改變的時候被改變。比如,由于安全你不希望這些對象被導航頁面改變而影響。沒有特别的可操作的改變可以影響實體層。

用例

軟體在這一層包含應用程式指定的業務規則。它封裝和實作了這個系統的所有用例。這些用例轉化資料流到實體和從實體轉化到資料流,然後這些實體直接使用它們企業範圍的業務規則來達到用例的目的。

我們不期望改變這一層來影響到實體。我們也不希望這一層被外部的改變,比如資料庫、UI、任何常見的架構等影響。在這一層在這的觀點上是被孤立的。

然而,我們要做的是希望應用程式操作的改變會影響到軟體的用例,是以應該是在這一層。如果用例的細節改變了,那麼這一層的某些代碼當然也會受到影響。

接口擴充卡

在軟體的這一層是一系列的擴充卡,通過最友善的用例和實體轉換資料格式,最友善的外部代理格式有資料庫或者Web等。在這一層,舉個例子,将會完整包含 GUI 的 MVC 架構。Presenters、Views、 Controllers 都屬于這裡。Models有可能僅僅是資料結構,它們從 contrllers 被傳入到用例中,然後從用例到 presenters 和 views。

同樣的,在這一層,資料會被轉換,從最友善的方式如實體和用例,轉換到使用的用于持久架構的最友善的方式,即資料庫。

這個圓中沒有代碼可以向内知道任何關于資料庫的東西。如果資料庫是一個 SQL database,那麼所有 SQL 應該在這一層被限制,特别是在這一層需要進行資料庫操作的部分。

在這一層也有必要從一些外部形式去轉換資料,比如一個外部服務,轉化為内部用例和實體使用的形式。

架構和驅動

最外層通常是架構和工具,通常是資料庫、Web 架構等的組合。通常你不需要在這一層編寫太多的代碼,除了一些用于向内圈進行通信的固定代碼。

這一層是所有細節走向的地方。Web是一個細節。資料庫是一個細節。我們把這些東西放置在外部,這樣它們就難以造成傷害。

隻有四個圓?

不,這些圓隻是簡圖。你可能會需要多于這四個圓。這裡并沒有一個規則來讓你必須要使用這四個圓。然而,依賴規則 總是适用的。代碼依賴總是指向内部。當你向内移動時,抽象級别增加。最外層的圓是最低級别的具體細節。當你向内移動,就會變得更加抽象和更進階别政策的封裝。最内部的圓是最通用的。

跨越邊界

右下方的圖是一個我們怎麼去跨越圓的邊界的例子。它展示了 Controllers 和 Presenters 通過下一層使用用例進行通信。注意控制流。它在controller中開始,通過用例,然後再Presenter中執行。也要注意代碼的依賴。它們每一個都是向内指向用例。

我們通常使用 依賴反轉準則 來解決這個明顯的沖突。在像Java的語言中,舉個例子,我們使用了interfaces和繼承關系,這樣代碼依賴關系控制權被反轉,達到跨越邊界的目的。

舉個例子,考慮到用例需要調用Presenter。然而,這些調用必須不能是直接的,因為它會違反_依賴準則_:内部圓不能提到外部圓中的任何名字。是以我們這種情況我們會調用在内部圓中的接口(就像這裡展示的 Use Case Output Port)),然後在外部圓中的Presenter去實作它。

同樣的技術在架構的所有跨越邊界的地方被使用到。我們利用動态代理的優勢去建立代碼依賴來達到控制反轉,是以我們可以確定無論什麼方向的 依賴準則 控制流都能有效。

什麼資料跨越邊界。

典型的跨越邊界的資料就是簡單的資料結構。如果你喜歡你可以使用基本結構或者簡單的資料傳輸對象。或者是方法調用時的簡單的參數資料。或者你可以把它放進一個HashMap,或者建構它到一個對象中。重要的是分離的、簡單的資料結構跨越過邊界。我們不希望去欺騙并傳遞 Entities 或者資料庫的行。我們不希望資料結構有任何的違反 依賴準則 的依賴。

舉個例子,很多資料庫架構通過一個查詢傳回一個友善的資料形式的響應。我們可能稱它為一個RowStructure。我們不希望這個行結構向内跨越邊界。這将違反 依賴準則 因為這将強制一個内部圓去知道外部圓的一些東西。

是以當我們跨越邊界傳遞資料時,它對于内圓來說總是以最友善的形式的。

遵守這些簡單的規則并不難,前進的道路上會解決很多頭疼的事情。通過分離軟體到不同層次,并遵守 依賴準則 ,你将會建立一個本質上可測試的系統,這意味這所有的好處。當任何系統外置的部分變得過時,就像資料庫,或者web架構,你可以在最小的改動下替換這些外置元素。