摘要:依賴規則同心圓表示軟件的不同部分。但是,這個調用不能直接進行因為會違反依賴規則。我們使用動態多態來創建與控制流相反的源碼依賴,以便于無論在控制流的任何方向都不會違反依賴規則。這會導致內部圈需要知道外部圈的內容進而違反依賴規則。
Robert C. Martin (Uncle Bob)
原文:https://blog.cleancoder.com/u...
譯:祝坤榮
在過去幾年我們看到關于系統架構的很多想法。這些包括:
Alistair Cockburn的六邊形架構(也叫做端口與適配器),Steve Freeman, 和 Nat Pryce在他們精彩的著作Growing Object Oriented Software(http://www.amazon.com/Growing...。
Jeffrey Palermo的Onion Architecture (http://jeffreypalermo.com/blo...
去年一片博客里的 Screaming Architecture (http://blog.cleancoders.com/2...
James Coplien 與Trygve Reenskaug的DCI(http://www.amazon.com/Lean-Ar...。
Ivar Jacobson的書: Object Oriented Software Engineering: A Use-Case Driven Approach 的BCE(http://www.amazon.com/Object-...)
盡管這些架構在一些細節上都有不同,它們仍是相似的。他們都有同樣的目標,隔離關注點。他們都通過將軟件分層來達到隔離。每個都至少有一層業務規則,另一層作為接口。
每個這些架構產出的系統都是:
獨立的框架。架構不依賴一些存在類庫的特性。這樣你可以像工具一樣使用這種框架,而不需要讓你的系統受到它的約束條件。
可測試。業務規則可以脫離UI,數據庫,web服務器或其他外部元素進行測試。
獨立的UI。UI可以很容易的更換,系統的其他部分不需要變更。例如,Web UI可以被換成控制臺UI,不需要變更業務規則。
獨立的數據庫。你可以交換Oracle或SQL Server,用于Mongo,BigTable,CouchDB或其他的東西。你的業務規則不與數據庫綁定。
獨立的外部代理。實際你的業務規則并不知道關于外部世界的任何事情。
這篇文章上面的圖試著將以上所有架構整合成一個可執行的想法。
依賴規則同心圓表示軟件的不同部分。大體上,你走的越遠,軟件的級別更高。外部的圓是機制,內部的圓是策略。
讓這個架構工作的覆蓋規則是依賴規則。這個規則說明了源代碼依賴只能向內。內部圓不能知道任何外部圓的事。實踐中,外部圓里一些聲明的名字不能被內部圓里的代碼提到。這包括,函數,類,變量或其他任何軟件實體。
同樣的,外部圓使用的數據格式不應該被內部圓使用,尤其是當這些格式是被外部圓使用的框架生成的時候。我們不想讓外部圓的東西影響到內部圓。
實體實體封裝企業域范圍的業務規則。實體可以是一個有方法的對象,也可以是一組數據結構和函數。只要企業里不同的應用可以使用這些實體就可以。
如果你不是企業級,而只是寫一個單體應用,那么這些實體就是應用的業務對象。它們封裝了最通用和高層的規則。當外部變化時它們基本不太會變化。例如,你不會認為這些對象會因為頁面導航或安全方面的變化而改變。任何特定應用的操作都不應該影響實體層。
用例這層的軟件包含特定應用的業務規則。它封裝并實現了系統的所有用例。這些用例組織了實體中的數據流向,并指揮這些實體使用他們的企業域業務規則來完成用例的目標。
我們不期望這層影響實體。我們也不希望這層會在如數據庫,UI,或其他常用框架這樣的外部變化時被影響。這層隔離了以上關注點。
當然我們期望對于應用操作的變化會影響用例而進一步影響到這層的軟件。 如果一個用例的細節變化了,那么這層的代碼肯定也會被影響。
接口適配器這層的軟件是一組適配器,其將數據轉換成從用例和實體最合適的格式,到對于一些類似數據庫或網站這種外部設施最合適的格式。在這一層,舉個例子,會包含GUI的MVC架構。Presenters, Views,與Controllers都屬于這里。模型基本就是從controllers傳遞到用例的數據結構,并從用例返回到presenters和views。
類似的,數據被轉換了,在這層,從對于實體和用例合適的結構,變成對于持久層框架使用的結構。這圈內的代碼不應該知道數據庫。如果數據庫是一個SQL數據庫,那么所有SQL都應該在這層內,特別是此層與數據庫有關的部分。
這層其他適配器也需要將數據從類似外部服務的外部的結構,轉換成用例和實體使用的內部結構。
框架與驅動最外層主要組合了數據庫,網絡框架這樣的框架和工具。在這層你除了寫一些與內層環通信的膠水代碼,基本不會有其他代碼。
這層是所有細節存在的地方。網絡是細節。數據庫是細節。 我們將這些東西放在外部保證它們不會影響其他部分。
只有四個圈?不是的,圓圈是個示意。你可能發現你需要不止4個。沒有規則說你一定要有四個。 實際上,依賴規則一直存在。源代碼依賴一直指向內部。當你向內部移動時抽象的層次在增加。最外部的圓是很低層的具體細節。當你內移時軟件變得更抽象,并封裝了高一級的策略規則。最內部的圓是最普遍的抽象層級。
跨越邊界在圖的右下方是我們穿越圓圈邊界的示例。它展示了Controller和Presenter與下一層的用例進行通信。注意控制流。它從controller出發,穿過用例,然后在presenter里執行。也注意下源碼依賴。它們每個都指向內部的用例。
我們通常使用依賴反轉原則解決這個明顯的問題。在java這樣的語言中,我們會整理源碼依賴與控制流相反的接口和繼承關系,讓它們從邊界正確的穿過。
例如,用例需要調用presenter。但是,這個調用不能直接進行因為會違反依賴規則。外圈的名字不能被內圈提到。所以我們的用例調用內圈的一個接口(在這個例子里是Use Case Output Port),并讓外圈的presenter實現它。
架構里所有的邊界穿越都用這個技巧。我們使用動態多態來創建與控制流相反的源碼依賴,以便于無論在控制流的任何方向都不會違反依賴規則。
什么樣的數據會穿越邊界正常來說穿過邊界的數據是簡單數據結構。你可以使用基本結構或簡單的Data Transfer 對象。或者可以方便的進行函數賦值的數據。或者你可以打包進一個hashmap,或者將它組裝成一個對象。重要的是穿過邊界的是隔離,簡單的數據結構。我們不想搞變通傳遞實體或數據庫行數據。我們不想數據結構有任何違反依賴規則的依賴。
例如,很多數據庫框架在查詢后返回一個方便的數據格式。我們可以叫它RowStructure(行結構)。我們不想將這個行機構通過邊界傳遞給內部的圈。這會導致內部圈需要知道外部圈的內容進而違反依賴規則。
所以當我們在邊界傳遞數據是,要注意其應該是內部圈的格式。
結論遵從這些簡單規則并不難,并且能幫你減少以后的問題。通過將軟件隔離分層,并遵從依賴規則,你可以建立一個真正可測試的系統,包含了以上所有好處。當任何系統額外部部分過時了,比如數據庫或web框架,你可以容易的替換這些過時的元素。
—
本文來自微信公眾號「麥芽面包,id「darkjune_think」轉載請注明。
交流Email: zhukunrong@yeah.net
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74206.html
過去幾年,我們已經看到了一系列關于系統架構的想法,包括:六邊形架構(接口與適配器)洋蔥架構(Onion Architecture)Screaming ArchitectureDCIBCE這些架構有很多共同的點(思想),盡管它們細節上有所不區別,它們都有相同的目標,那就是關注點分離(the speration of concerns), 它們都是通過將軟件分層來實現這種分離,每個組件至少有一個用于業...
摘要:業務層,業務層,是最為核心的一層。對于和的狀態保存恢復也通過處理。對于的綁定操作和命令操作都是暴露的,也易于測試。需要注意的是標簽的節點中要使用到根節點中標簽里設置的的話需要這樣設置抽象類中設置了和注解,只起到清晰提醒作用。 原文發表于:Rockos blog(rocko.xyz)] - MVVM_Android-CleanArchitecture 前言 Architecture is...
閱讀 2418·2021-11-16 11:44
閱讀 852·2021-09-10 11:16
閱讀 2228·2019-08-30 15:54
閱讀 1057·2019-08-30 15:53
閱讀 1900·2019-08-30 13:00
閱讀 620·2019-08-29 17:07
閱讀 3514·2019-08-29 16:39
閱讀 3138·2019-08-29 13:30