[IT] DDD, Hexagonal, Onion, Clean, CQRS 大整合

本文是介紹我如何將 DDD, Hexagonal, Onion, Clean, CQRS Architecture 等架構概念整合在一起,我將它命名為 Explicit Architecture。上述的概念基本上都是通過了市場的試驗,並在許多高要求的平台上被應用。 系統的基本組件 首先回顧 EBI 與 Ports & Adapter 架構。這兩種架構都明確區分了哪些程式碼是應用程式的內部,哪些是外部,以及哪些是連接內部和外部的程式碼。 Ports & Adapters 明確地定義出了系統的三個部分: 使用者介面 (User Interface, UI) 商業邏輯(business logic)、應用程式核心(application core) 基礎設施(Infrastructure),如 DB、搜尋引擎或第三方API等工具。 我們真正應該關心的是應用程式的核心,這是讓我們的程式碼能夠完成其應有功能的程式碼。它可能會使用多種 UI(網頁、手機、CLI、API 等等),但實際執行工作的程式碼是相同的,並位於應用程式的核心,觸發它的 UI 實際上並不重要。 一個典型的應用程式流程從 UI 的程式碼開始,經過應用程式核心到基礎設施程式碼,再回到應用程式核心,最後將回應傳遞給 UI。 工具 Tools 工具指的是那些遠離我們系統核心程式碼,但為我們應用程式所用的工具,例如,DB、搜尋引擎、網頁伺服器或 CLI 控制台(儘管後兩者也是交付機制)。 雖然將 CLI 與 DB 分類在一起可能有些奇怪,儘管它們有不同的目的,但實際上它們都是應用程式使用的工具。關鍵的區別在於,CLI 和網頁服務器用於告訴我們的應用程式做些什麼,而 DB 則由我們的應用程式告訴它做些什麼。這是一個非常重要的區別,因為它對我們如何建構連接這些工具與應用程式核心的程式碼有著強烈的影響。 將工具和傳遞機制連接到應用程式核心 連接工具與應用程式核心的程式碼單元被稱為適配器(Ports & Adapters Architecture),適配器實現了將業務邏輯與特定工具進行通訊。 告知我們應用程式應該做什麼事的適配器稱為 Primary 或 Driving Adapters; 被我們應用程式告知應該做什麼事的適配器稱為 Secondary or Driven Adapters。 ...

<span title='2023-11-06 23:38:13 +0800 +0800'>November 6, 2023</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;hgraca

[IT] 服務導向的架構 Service Oriented Architecture (SOA)

The SOA Style has been around since the late 1980s and has its origins in ideas introduced by CORBA, DCOM, DCE and others. Much has been said about SOA, and there are a few different implementation patterns but, in essence, SOA focuses on only a few concepts and doesn’t give any prescription on how to implement them: SOA風格自 1980 年代晚期便一直存在,其起源於 CORBA、DCOM、DCE 等等。關於 SOA 已經有很多討論,且有許多不同的實作,但本質上,SOA 只關注少數幾個概念,而且指引我們如何去實作: 使用者導向的應用程式。 可重複使用的商業服務。 獨立的技術堆。 自治性(獨立演進、可擴展性與可部署性) SOA 是一最獨立於任何技術或產品的架構原則,就像多態與封裝一樣。 ...

<span title='2023-11-02 22:32:25 +0800 +0800'>November 2, 2023</span>&nbsp;·&nbsp;5 min&nbsp;·&nbsp;hgraca

[IT] 從 CQS 到 CQRS

當我們有一個以數據為中心的應用程式,即只實現基本的 CRUD 操作,並將業務流程(即要更改的數據和更改的順序)留給用戶時,好處是用戶可以在不需要更改應用程式的情況下更改業務流程。另一方面,這意味著所有用戶都需要知道所有可以使用該應用程式執行業務流程的所有細節,這在沒有明確的規範且有大量人員參與其中時,將會是一個大問題。 在一個以數據為中心的應用程式中,該應用程式對業務流程一無所知,因此該 domain 無法擁有任何「動詞」,也就是說,應用程式本身無法做出除了改變原始數據以外的任何事情。它變成了數據模型(data model)的高度抽象。這些流程只存在於應用程式用戶的腦海中,或者甚至存在於釘在電腦螢幕上的便利貼中。 一個非凡且實用的應用程式旨在減輕使用者的「流程」負擔,透過捕捉他們的意圖,使其成為一個能夠處理行為的應用程式,而不僅僅是儲存數據。 CQRS is the result of an evolution of several technical concepts that work together to help provide the application with an accurate reflection of the domain, while overcoming common technical limitations. CQRS 是許多技術概念演變的結果,這些概念使應用程式能準確地反映領域(domain),並同時克服常見的技術限制。 命令查詢分離 Command Query Separation 正如 Martin Fowler 所述,「命令查詢分離」這個術語是由 Bertrand Meyer 在他的《物件導向軟體建構(Object Oriented Software Construction)》(1988年)中首次提出的 - 這本書被認為是物件導向早期最具影響力的書籍之一。 梅爾認為,作為一個原則,我們不應該有既改變數據又返回數據的方法。因此,我們有兩種類型的方法: Queries(查詢):返回數據但不更改數據,因此沒有副作用; Commands(指令):更改數據,但不返回數據。 換句話說,提問不應改變答案,而行動也不應回饋答案,這同時也有助於尊重單一責任原則。 然而,有些模式是這條規則的例外,傳統的佇列和堆疊會彈出在佇列或堆疊中的元素,既改變了佇列或堆疊,也返回了從中移除的元素。 命令模式 Command Pattern 命令模式的主要概念是將我們從資料中心的應用程式轉移到以流程為中心的應用程式,具有領域知識和應用程式流程知識。 在實際操作中,這意味著我們不再讓使用者執行 CreateUser, ActivateUser 和 SendUserCreatedEmail 這三個動作,而是讓使用者直接執行一個 RegisterUser 的指令,這個指令將會執行前述的三個動作,作為一個封裝的業務流程。 ...

<span title='2023-11-01 23:29:40 +0800 +0800'>November 1, 2023</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;hgraca

[IT] 事件驅動架構 Event-Driven Architecture

利用事件來設計應用程式似乎是 80年代後期開始的一種做法,我們可以在使用事件在前端或後端任何地方使用事件,當按下一個按鈕,涉及某些數據變更,或是執行某些後端動作時。 What/When/Why 就像類別(classes)一樣,組件(components)之間應該保持低耦合,但在內部應保持高內聚。當組件需要協作時,比如說組件A需要觸發組件B中的某些邏輯,自然的做法就是讓組件A調用組件B中一個物件的方法。然而,如果A知道B的存在,那麼它們就是耦合的,A依賴於B,這使得系統更難改變和維護,事件可以用來防止耦合。 如果我們有一個團隊只專注於組件B的工作,它可以改變組件B對組件A邏輯的反應,甚至不需要與負責組件A的團隊溝通。組件可以獨立進化:我們的應用程式變得更有機(organic)。 即使在同一個組件中,有時我們會需要執行程式碼作為一個行動的結果,但它並不需要立即執行,也就是說,當事件的結果互不影響的情境下,我們可以採用 異步(async) 的策略執行程式。 然而,這樣做也存在危險,如果我們不加選擇地使用事件,可能會使一個概念上高度內聚的邏輯被解耦。換句話說,本應在一起的程式碼被強行分開,變得很難追蹤、理解(類似goto語句),最後使得它變成:speghetti code! 為了防止我們的程式碼變成一堆混亂的 speghetti code,我們應該清楚的限制事件的使用規則。根據我的經驗,有三種情況下應該使用事件: 解耦元件。 執行異步任務。 追蹤狀態變更 (audit log) 1. 解耦元件 當元件A執行需要觸發元件B邏輯的動作時,我們可以選擇不直接呼叫它,而是將一個事件發送到事件調度器(dispatcher)中。元件B將會在調度器中監聽該特定事件,並在事件發生時作出反應。 這意味著A和B都將依賴於調度器和事件,但他們將對彼此一無所知,也就是說他們是解耦的。 理想情況下,調度器和事件都不應存在於任何組件中: 調度器應該是一個與我們的應用程式完全獨立的庫,因此應該使用依賴性管理系統安裝在一個通用的位置。在PHP世界中,我們會使用 Composer 將之安裝在 vendor 的資料夾。(C# 可以參考我 EventBus 的文章) 這個事件雖然是我們應用程式的一部分,但應該存在於兩個組件之外,以保持它們對彼此一無所知。該事件在組件之間共享,並且是應用程式核心的一部分。事件是 DDD 所稱的 共享核心(Shared Kernel) 的一部分。這樣,兩個組件將依賴於共享核心,但將對彼此保持不知情。然而,在單體應用程式中,為了方便,可以將其放置在觸發事件的組件中。 Shared Kernel 共享核心 […] Designate with an explicit boundary some subset of the domain model that the teams agree to share. Keep this kernel small. […] This explicitly shared stuff has special status, and shouldn’t be changed without consultation with the other team. - Eric Evans 2014, Domain-Driven Design Reference […] 指定一個團隊同意共享的領域模型的子集,並明確劃定其範疇。保持這個核心部分的規模小。[…] 這些明確共享的內容具有特殊的地位,不應在未與其他團隊諮詢的情況下進行更改。 - 艾瑞克·埃文斯 2014,領域驅動設計參考資料 ...

<span title='2023-10-31 23:25:09 +0800 +0800'>October 31, 2023</span>&nbsp;·&nbsp;2 min&nbsp;·&nbsp;hgraca

[IT] 乾淨架構 Clean Architecture

Robert C. Martin (aka Uncle Bob) 在 2012 年在他的部落格上發表了他對於乾淨架構的想法,並在幾個會議上進行了關於乾淨架構的演講。 乾淨架構套用了許多為人熟知的概念、規則和模式,並解釋如何將它們組合在一起,以提出一種標準化的應用程式建構方式。 站在 EBI, Ports & Adapters 與洋蔥架構的肩膀上 乾淨架構背後的核心目標與 Ports & Adapters(六邊形)和洋蔥架構的目標是相同的: 工具的獨立性。 交付機制的獨立性。 獨立測試的可行性。 在發布有關乾淨架構的文章中,這是用來解釋整體概念的一張圖: 正如 Uncle Bob 在他的文章中所說,上面這張圖嘗試將最新的架構思想整合成一個可行的概念。 讓我們將乾淨架構的圖表與用來解釋六角架構和洋蔥架構的圖表進行比較,看看它們在哪些地方相符: 工具和交付機制的外部化 六角形架構專注於將工具和交付機制從應用程式中外部化,使用介面(ports))和適配器(adapters)。這也是洋蔥架構的核心價值之一,如圖所見,UI、基礎設施和測試都在圖表的最外層。乾淨的架構具有完全相同的特性,將 UI、Web、DB 等都放在最外層。最後,所有應用程式核心程式碼都是與框架、庫獨立的。 依賴方向 在六角架構中,我們並沒有任何明確的指示告訴我們依賴性的方向。然而,我們可以輕易地推斷出來:應用程式有一個埠(或介面),必須由一個適配器來實現或使用。因此,適配器依賴於介面,它依賴於位於中心的應用程式。外部的東西依賴於內部的東西,依賴性的方向是朝向中心。在洋蔥架構圖中,我們也沒有任何明確的指示告訴我們依賴性的方向,然而,在他的第二篇文章中,Jeffrey Palermo 非常清楚地說明所有的依賴性都是朝向中心。乾淨架構則是非常明確地指出依賴性方向是朝向中心。他們都在架構層面引入了依賴反轉原則。內圈中的任何東西都不能知道外圈中的任何東西。此外,當我們跨越邊界傳遞數據時,它總是以對內圈來說最方便的形式存在。 分層 六角形架構圖只顯示了兩層:應用程式的內部和外部。然而,洋蔥架構則將 DDD 中 application layer 融入其中:application service 持有用例邏輯(use case logic);domain service 封裝不屬於實體或價值對象的領域邏輯。與洋蔥架構相比,乾淨架構保留了 application layer(use case)和 entities layer,但似乎忽略了 domain service layer。然而,閱讀 Uncle Bob 的文章後,我們意識到他認為一個 entity 不僅是 DDD 意義上的 entity,而且是任何 domain object:「一個實體可以是一個帶有方法的物件,或者可以是一組數據結構和函數。」實際上,他合併了這兩個最內層的層級以簡化圖表。 ...

<span title='2023-10-29 21:11:58 +0800 +0800'>October 29, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 洋蔥架構 Onion Architecture

洋蔥架構 洋蔥架構是由 Jeffrey Palermo 在 2008 年提出的。在我看來,它是基於 Ports & Adapters 架構的構念,將領域(domain)放在應用程式的中心,將交付機制(UI)和系統使用的基礎設施(infrastructure, ex.ORM,搜索引擎,第三方API等)。差別是,它對內部進行了分層。 我們從分層架構學習到最基本的分層通常有: Presentation 呈現層 Application 應用層 Domain 領域層 Persistence 持久層 而 Ports & Adapters 架構隱含了兩個同心層: 外部:傳遞機制(delivery mechanisms)與基礎設施(infrastructure) 內部:業務邏輯 Ports & Adapters 和 Onion Architecture 同時擁有一個概念,那就是通過編寫適配器(adapter),將應用程式的核心與基礎設施隔離,以防止基礎設施滲透到應用程式核心中(意思是應用程式核心直接對基礎設施產生依賴)。這使得抽換應用程式使用的工具和交付機制變得更容易,提供了一些對技術、工具和供應商鎖定的保護。 這也賦予應用程式一種愉快的能力,即無需真實的基礎設施或交付機制就能運行,因為它們可以被模擬物件所替換,這易於進行程式碼的測試。 然而,洋蔥架構也告訴我們,在企業應用中,我們將不只有內部與外部這樣簡單的分層,在內部,也就是業務邏輯中,我們會增加一些我們從領域驅動設計(DDD)認識的一些層: 此外,它明確地闡述了 Ports & Adapters 架構中關於依賴方向的隱含概念: 外層依賴於內層, 內層對外層一無所知。 這意味著耦合的方向是朝向中心,為我們提供了一個獨立的物件模型(domain model),其核心不依賴任何東西。我們有足夠的靈活性可以改變外層,而不影響內層,更重要的層面。它在架構層面上運用了依賴反轉原則。 洋蔥架構的主要原則: 應用程式是建立在一個獨立的物件模型周圍。 內層定義介面,外層實現介面。 耦合的方向是朝向中心。 所有應用程式的核心代碼都可以獨立於基礎設施進行編譯和運行 此外,任何外層都可以直接呼叫任何內層,這不會破壞耦合方向,並避免創建僅包含無業務邏輯的 proxy methods,甚至是 proxy classes,僅為了符合某種分層方案。這也符合 Martin Fowler 所表達的。 […] the layers above can use any layer beneath them, not just the layer immediately beneath. - Jeffrey Palermo 2008, The Onion Architecture: part 3 […] 上層的層次可以使用任何位於其下的層次,而不僅僅是直接下方的那一層。 - 傑弗里‧巴勒莫 2008,洋蔥架構:第三部分 ...

<span title='2023-10-29 20:34:57 +0800 +0800'>October 29, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[ML] sample1 - 手寫數字辨識

MNIST NIST(National Insitute of Standards and Technology) 是美國國家標準與技術研究院,MNIST 是由 NIST 所提供的一組經典的機器學習測資,可以想成是深度學習中的「Hello World!」,它由 60000張 訓練圖片與 10000 張測試圖片所組成,為手寫數字的灰階圖片,大小為 28 * 28 像素,分類 0 到 9 共 10 個數字。 可透過 keras 模組直接取得資料 >>> from tensorflow.keras.datasets import mnist 輸入 mnist.load_data() 可取得 mnist 資料集,回傳值為 2*2 的 tuple of ndarray。 >>> (train_images, train_labels), (test_images, test_labels) = mnist.load_data() tuple 裡面裝載的是 NumPy 的 ndarray 物件,我們可以利用 o.shape 來取得 ndarray 的屬性 len(o) 來取得陣列的個數 >>> train_images.shape (60000, 28, 28) # 3 軸陣列,其大小為 60000 * 28 * 28 >>> test_images.shape (10000, 28, 28) # 3 軸陣列,其大小為 10000 * 28 * 28 >>> len(train_labels), len(test_labels) (60000, 10000) # 訓練集與測試集各有 60000 與 10000 筆 labels >>> train_labels array([5, 0, 4, ..., 5, 6, 8], dtype=uint8) # train_labels 裝 60000 筆資料對應的解答(0-9 的數字) 我們可以利用 matlabplot 把圖片印出來看看 plt.matshow(train_images[0], cmap = plt.get_cmap('gray')) plt.show() 用 Dense 層建構神經網路 首先我們需要建立神經網路架構,層(layer)是組成神經網路的基本元件,一個層就是一個資料處理的模組。具體而言,每一層都會從資料中萃取出特定的轉換或表示法,經過數層的資料萃取(data distillation)後,將資料「過瀘」成最後特定的轉換或表達(representation)。 ...

<span title='2023-10-28 14:11:35 +0800 +0800'>October 28, 2023</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;Rain Hu

[IT] 埠與適配器架構 Ports & Adapters Architecture aka 六邊形架構 Hexagonal Architecture

埠與適配器架構(又稱為六角架構)是由 Alistair Cockburn 所構想,並在 2005 年於他的部落格中寫下。這是他用一句話定義其目標的方式: Allow an application to equally be driven by users, programs, automated test or batch scripts, and to be developed and tested in isolation from its eventual run-time devices and databases. - Alistair Cockburn 2005, Ports and Adapters 允許一個應用程式能夠同等地由用戶、程式、自動化測試或批次腳本驅動,並且能夠在與其最終運行時間設備和數據庫隔離的情況下進行開發和測試。 - 艾利斯特·科本 2005,端口與適配器 我看過一些討論「Ports & Adapters 架構」的文章,其中大量提到了分層(layers)。然而,在原始的 Alistair Cockburn 的文章中,我並未讀到任何關於分層的內容。 Ports & Adapters 架構的思想是將我們的應用程式視為系統的中心產物,所有的輸入和輸出都通過一個端口進入/離開應用程式,該端口將應用程式與外部工具、技術和交付機制隔離。應用程式應該對誰/什麼正在發送輸入或接收其輸出一無所知。這旨在提供一些保護,以防止技術和業務需求的演變,促使產品在開發完成後不久就因為技術/供應商的封鎖而變得過時。 在這篇文章中,我們將深入探討以下主題: 傳統方法的問題 傳統的方法在前後兩端都可能帶來問題。 在前端方面,我們最終會有業務邏輯滲透到 UI 中(例如,當我們在控制器或視圖中放置用例邏輯,使其在其他 UI 中無法重用)或甚至是 UI 滲透到業務邏輯中(例如,由於我們在模板中需要一些邏輯,因此我們在我們的實體中創建方法)。 在後端方面,我們可能會有外部庫和技術滲透到業務邏輯中,因為我們可能會透過類型提示、子類別化,甚至在我們的業務邏輯內實例化庫類別來直接引用它們。 ...

<span title='2023-10-27 23:46:32 +0800 +0800'>October 27, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 領域驅動設計 Domain-Driven Design

「領域驅動設計」這個詞是在 Eric Evans 在他的著作《領域驅動設計:解決軟體核心的複雜性(Domain-Driven Design: Tackling complexity in the Heart of Software, 2003)》中所提出的,書中正式地提出了許多軟體開發的概念。 我無法用一篇文章就概括 DDD,與 DDD 相關的重要概念實在太多了。以下列出我認為一些重要的 DDD 概成,包含: Ubiquitous Language Layers Bounded Contexts Anti-Corruption Layer Shared Kernel Generic Subdomain 通用語言 Ubiquitous Language 在軟體開發中,一個常見的問題是怎麼理解程式碼,它是什麼,它能做什麼,它如何做,它為什麼這樣做…如果程式碼使用的術語與領域專家使用的術語不同,理解程式碼便變得更加複雜。例如,如果領域專家談論的是 elder users,而程式碼中提到的是 supervisors,那麼名詞可能就會造成在討論應用程式時造成混淆。然而,大部分的模糊性可以透過適當地命名類別和方法來解決,讓類別的命名明確地表達出物件是什麼,讓方法的命名明確地表達出方法在領域上下文中做了什麼事。 使用通用語言的主要概念是將應用程式與商業邏輯對齊,這是通過在程式碼中採用業務與技術之間的共同語言所實現的。該語言的來源是公司的業務部門,它們擁有需要實施的概念,但術語則與公司的技術部門協商(這意味著業務部門並不總是選擇最佳命名)以創建一種業務與程式開發人員共通且不會發生歧義的共同語言。包含程式碼、類別、方法、屬性和模組的命名都最重與通用語言對齊。 層 Layers 我在之前的文章中已經談過分層的概念,但我認為此刻重提由DDD所識別的各層是很重要的 User Interface 使用者介面 負責繪製用戶與應用程式互動的螢幕,並將用戶的輸入轉換為應用程式命令。值得注意的是,「用戶」可以是人類,但也可以是連接到我們API的其他應用程式,這完全對應於EBI架構中的邊界對象。 Application Layer 應用層 協調領域對象以執行用戶所需的任務:用例。它不包含業務邏輯。這與EBI架構中的互動者相關,只是互動者是與UI或實體無關的任何對象,而在這種情況下,應用層只包含與用例相關的對象。這一層是應用服務所屬的地方,因為它們是用例協調發生的容器,使用存儲庫、領域服務、實體、價值對象或任何其他領域對象。 Domain Layer 領域層 這是包含所有業務邏輯的層,包括領域服務、實體、事件以及任何其他包含業務邏輯的對象類型。顯然,它與EBI的實體對象類型有關。這是系統的核心。領域服務將包含不完全適合於實體的領域邏輯,通常在完成某些領域動作時協調多個實體。 Infrastructure 基礎建設 支援上層的技術能力,即持久性或訊息傳遞。 有界上下文 在企業應用中,模型可能會大幅增長,同時進行程式碼開發的團隊規模也可能會擴大。這帶來了兩個問題: 開發人員必須處理的程式碼庫越大,認知負荷就越大,理解程式碼的難度也就越高,因此可能會引入更多的錯誤和判斷失誤, 越多的開發人員在同一個程式碼庫上工作,就愈難協調對應用程式的共同技術與領域視野。 換句話說,手頭的問題變得過於龐大。 對於大問題的常見解決方案是將其分解成較小的部分,這正是「有界上下文」發揮作用的地方。 Two subsystems commonly serve very different user communities - Eric Evans 2014, Domain-Driven Design Reference 兩個子系統通常服務於截然不同的用戶群體 - Eric Evans 2014,領域驅動設計參考 ...

<span title='2023-10-26 23:11:53 +0800 +0800'>October 26, 2023</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;hgraca

[IT] 套件與命名空間 packaging & namespacing

系統的架構是該系統的高層視圖、大局觀,以粗略的筆觸描繪的系統設計。架構決策是系統中的結構性決策,影響整個程式庫的決策,也是定義其他所有元素將在其上建立的決策。 架構決定系統的許多元素,包含: 組件 Components 組件之間的關係 Relationships between components 指導組件與組件間關係如何設計與如何演化 Principles guiding the design and evolution of components and relationships 換句話說,這些是隨著系統演進更難改變的設計決策,它們是支撐功能開發的基礎。 義大利麵架構 Spaghetti Architecture 有些專案,結構隨機,既不反映架構,也不反映領域。如果我問「我應該把這個 value object 放在哪裡?」結果得到「把它放在 src 資料夾的某個地方」這樣的回答;如果我問「執行這個邏輯的 service 在哪裡?」卻得到「用你的 IDE 進行搜索」這樣的回答。這意味著專案沒有經過組織,這樣鬆散的結構就稱為義大利麵架構(Spaghetti Architecture)。 這是一個大問題,因為這意味著沒有套件模組化,高階的程式碼關係和流程並沒有可以遵循的邏輯結構,導致模組之間高度耦合且低內聚,實際上可能代表根本沒有模組,應該屬於模組的程式碼散佈在整個程式碼庫中。 可維護的程式碼庫 擁有一個可維護的程式庫意味著我們可以變更最少的程式碼來實現最大的概念變更。換句話說,當我們需要對一個程式碼單元進行變更時,我們應該盡可能少地對其他程式碼單元進行變更。 這樣帶來的優點有: 程式碼的修改變得簡單,因為它們對較少的程式碼產生影響。 程式碼的修改會更快,因為需要修改的程式碼較少。 因為修改的程式碼變少,出現錯誤的可能性也更低。 封裝(encapsulation)、**低耦合(low coupling)和高內聚(high cohesion)**是使程式碼隔離的核心原則,使得我們能夠擁有可維護的程式碼基礎。 封裝 Encapsulation 這是隱藏類別的內部訊息與實作的過程。 也就是說,它對外隱藏了實作的方式,使得一個類別的內部結構可以自由變更,而不會影響使用這個特定類別的其他類別。 低耦合 Low coupling 耦合是指一個程式碼單元與另一個程式碼單元的關係。如果對一個模組的更改將導致對另一個模組的更改,則該模組被認為與另一個模組高度耦合。而如果一個模組獨立於任何其他模組,則該模組被認為是低耦合的。這可以通過擁有一個穩定的介面來實現,有效地隱藏了對其他模組的實現。 低耦合的好處 可維護性(maintainability) - 變更僅限於單一模組 可測試性(testability) - 可以將單元測試涉及的模塊限制到最小 可讀性(readability) - 需要分析的類別被保持在最小範圍內 高內聚 High Cohesion 內聚性是指一個模塊的功能之間的緊密相關程度的衡量。低內聚是指模組間具有許多不相關的職責;高內聚是指模組間有類似的概念。 高內聚的好處 可讀性(readability) - 相關的功能都包含在單一模塊中 可維護性(maintainability) - bug 通常會被限制在單一模組中 重用性(reusability) - 專注於類別的功能,不被無用的功能污染 結構上的影響 前述的原則通常與類別相關,然而,它們對於類別群組也同樣適用。類別群組在一般情況下被稱為套件(package),但如果它們具有純粹的功能性目標(例如 ORM),我們可以更具體地稱呼它們為模組(module);如果它們具有領域目標(例如 AccountManagement),我們可以稱呼它們為組件(components)。這與 Bass, Clements 和 Kazman 在他們的書《實踐軟體架構(Software Architecture in Practice)》中解釋的定義是一致的。 ...

<span title='2023-10-23 23:14:14 +0800 +0800'>October 23, 2023</span>&nbsp;·&nbsp;3 min&nbsp;·&nbsp;hgraca