單一職責原則:軟體世界中最重要的規則 - DZone

banq發表於2021-11-05

單一職責原則SRP,這是整個軟體世界中最重要的單一規則。它幾乎可以在我們系統的所有級別上看到:從單個類到整個應用程式(無論使用的規模和架構如何)設計。

 

什麼是單一職責原則

可能你們中的大多數人將 SRP 解釋與一個宣告聯絡起來——類應該只做一件事。這是對這條規則最常見的解釋,但不幸的是,它並不是 100% 符合作者所寫的。

這條規則是由 Robert C. Martin 定義的,最初是這樣說的:“一個類應該只有一個改變的理由”。在他後來的一本書中,作者將其提煉為:“一個模組應該對一個且只有一個角色負責”。術語模組最初被定義為單個原始檔,而參與者被定義為對特定更改感興趣的一個人或一組人。正如您所看到的,這兩個定義與單一職責原則的最常見解釋大不相同,但實際上,這使其在某些地方更加明顯。

此外,SRP 表示這種方式與模組的業務職責更相關,而不是模組內部的確切程式碼。它還旨在將我們的注意力轉移到思考為什麼一段特定的程式碼在做某事,而不是實際上只考慮一段程式碼需要做什麼。這種視角的改變可以帶來很大的幫助,並幫助我們以不同的方式看待我們的系統。也許我們甚至在應用程式的某些部分看到了新的含義。

我們將首先描述單個類何時符合 SRP。

 

當類符合單一職責原則時

當我們按照 Robert C. Martin 的理解將單個類(確切地說是單個公共類;我假設我們在單個 . 檔案中有一個這樣的類)作為模組時。

最重要的是,理論上,該類可以做不止一件事——例如,在某些業務邏輯旁邊執行資料庫操作。不幸的是,所有這些都有一個問題:只要所有這些操作都是圍繞單個業務功能構建的,它就可以以這種方式工作。因此,簡單來說,我們可以執行各種操作,只要它們只涉及我們域的一個部分。

例如,在轉賬的情況下,它應該只負責傳入轉賬,而不是同時負責傳出、傳入和國際轉賬。此外,在這個特定的例子中,我們的類設計必須足夠靈活,不需要在資料庫架構更新後進行更改。

在此之後,希望在類級別上下文中對 SRP 進行清楚的解釋,我們將繼續描述我們可以注意到的 SRP 和 DDD 之間的關係。

 

單一職責原則和領域驅動設計

領域驅動設計就是了解現實生活中的領域,然後在我們開發的軟體中表達它們。它包含許多構建塊,從有界上下文等高階概念到實體或值類等更低階的東西。在我看來,整個 DDD 與 SRP 有很大關係,有意無意,幾乎所有前面提到的構建塊都符合。

例如——有界上下文。它應該強烈關注域的一個特定部分,因此從業務角度來看,它有一組特定的利益相關者負責其開發——它只有一個參與者。有界上下文表示不相關的概念的情況是不可能的,並且可能表明您的域建模中存在大問題。因此,如果我們決定將限界上下文視為一個模組,我們可以很快注意到,通過他的定義,它完全符合 SRP。當然,限界上下文之間可能存在依賴關係,但不應該出現一個限界上下文的更改需要其他任何一個限界上下文的更改的情況。出現此類問題可能表明您的域建模過程中出現了一些錯誤。

此外,我相信所有較小的 DDD 構建塊(如實體和值物件)都符合單一職責原則的定義。它們旨在為整個領域中的一個且僅一個概念建模。即使我們在設計限界上下文時失敗並將來自域內部的不相關概念混合在一起,實體內部仍然應該僅對域的一個元素進行建模。

在聚合的情況下,事情可能會更復雜。根據定義,哪個應該將較小的構建塊組合在一起,以便潛力和聚合可以對多個參與者負責。另一方面,聚合不應跨越多個有界上下文,因此較小的構建塊應該在域的同一部分內緊密耦合。

如您所見,在領域驅動設計的情況下,我們可以注意到很多 SRP 接觸。在有界上下文形式的戰略 DDD 和以實體或聚合等塊形式的戰術 DDD 中。

 

系統級單一職責原則

正如我之前提到的,我們可能不僅在小範圍內看到 SRP 的影響。它也可以在幾乎所有現代架構方法中注意到。事實上,無論您選擇使用單體應用程式還是面向服務架構的某種變體,或者是事件驅動的方法,如果您深入研究,您會注意到 SRP 對這些風格中的每一種的影響——如果是單體應用,它將會是一些損壞的 SRP。

我們將從簡單的舊單體及其與 SRP 的關係開始我們的旅程。在某種程度上,它將反映在其他一些架構風格中。

  • 單一職責原則和單體

    您可能知道,整體設計有 3 種主要方法——好的、壞的和醜陋的:

    1. 模組化單體,幾乎可以轉變為微服務。這裡我們應用程式中的每個模組負責整個系統提供的部分業務功能。如果您使用 DDD,那麼這些模組中的每一個都可能基於單獨的有界上下文。
    2. 分散式單體,嘗試實現微服務不幸失敗(大量同步通訊和分散式系統的所有問題);這種情況在某種程度上是另外兩種情況的結合。
    3. 傳統的單體應用,沒有明確拆分成模組,而是部署在一臺機器上,沒有分發,模組之間有很多直接依賴。

    如果您仔細閱讀以上部分,您可能會注意到每種方法和 SRP 之間的關係是不同的。另一方面,您可能還會注意到模組化單體至少受 SRP 的影響。

  • 面向服務的架構和單一職責原則

    在這種情況下,SRP 的影響是顯而易見的。基於它們中的任何一個的系統中的每個此類服務都應該負責整個域的特定部分,並且不應該出現來自不相關域的利益相關者負責引入其他域服務的更改的情況。

    基本上,這兩種架構都在一定程度上基於 SRP。確切地說,兩者都基於在服務之間拆分業務責任,而不是將它們混合在一個服務中。

  • 事件驅動架構和單一職責原則

    一開始,事件驅動系統中的模組是鬆散耦合的,幾乎沒有明確的直接依賴關係。事實上,在對系統中的特定服務進行更改時,除了服務交換的事件中的更新之外,其他服務應該不需要更改。基本上,這就是 SRP 在這種方法中的作用。

一般而言,如果架構風格基於自治、以功能為中心且鬆散耦合的模組/服務/部分,那麼您可以確定它在建立過程中的某個地方受到了 SRP 的影響。此外,您會注意到,大多數被認為是當今不錯選擇的架構方法在設計時都考慮了 SRP。如果這種影響是偶然的或有意識的,那是一個完全不同的話題,我認為它更多地是關於在元件之間拆分業務責任。

此外,構建模組之間高耦合或在單個模組中混合業務功能的應用程式似乎已經成為過去,並且是製作非常美味的義大利麵條的直接而清晰的方法。

 

相關文章