?軟件系統(tǒng)的復(fù)雜性、耦合度和內(nèi)聚性
我們遇到的任何軟件系統(tǒng)很可能都過于復(fù)雜,一次無法完全理解 — 人類的思維無法理解大量實體及其關(guān)系。我們傾向于通過建立抽象來簡化現(xiàn)實:一旦我們將許多閃亮的金屬、玻璃和橡膠定義為“汽車”,我們就可以談?wù)摗案咚俟贰薄ⅰ巴\噲觥焙汀俺丝汀?— 我們生活在我們創(chuàng)造的抽象世界中。同樣,我們編寫的軟件由服務(wù)、進程、文件、類、過程等組成 — 這些模塊隱藏了我們無法抗拒的一堆位和片段。讓我們思考一下。

概念與復(fù)雜性
任何系統(tǒng)都包括 概念 — 以其他概念為基礎(chǔ)定義的概念。例如,如果你正在實現(xiàn)一個電話簿,你會處理 名 和 姓、號碼、排序 和 搜索,這些概念是任何與電話簿相關(guān)的開發(fā)任務(wù)中必須牢記的 — 只因為電話簿的需求是用這些概念及其關(guān)系描述的。
在代碼中,高級概念被體現(xiàn)為服務(wù)、模塊或目錄,而較低級的概念則對應(yīng)于類、API 方法或源文件。
概念很重要,因為它們的數(shù)量(或相應(yīng)類和方法的數(shù)量)定義了系統(tǒng)的 復(fù)雜性 — 開發(fā)人員面對的認知負荷。如果程序員詳細了解他們正在處理的組件的行為,他們往往會變得極其高效,并且通常能夠為看似復(fù)雜的任務(wù)找到簡單的解決方案。否則,開發(fā)速度會很慢,并且需要進行大量測試,因為人們不確定他們的更改會如何影響系統(tǒng)的行為。

圖1:復(fù)雜性與實體數(shù)量相關(guān)
模塊、封裝和有界上下文
讓我們回到我們的例子。當你實現(xiàn)電話簿時,你會發(fā)現(xiàn)排序和搜索比你最初想象的要復(fù)雜得多。一旦你準備進入國際市場,你就會陷入深深的困境。一些電話服務(wù)提供商發(fā)送7位數(shù)字,其他人使用10位數(shù)字,還有些人使用13位數(shù)字(第一個字符為“+”或“0”)。德語有“?”,它與“ss”相同,而日語同時使用兩種字母表。一旦你開始閱讀標準,實現(xiàn)所有奇怪的行為并回應(yīng)用戶投訴,你會感到你的電話簿實現(xiàn)淹沒在充滿特殊情況的外語字母表的無關(guān)邏輯中。你需要 封裝。
引入 模塊。模塊封裝了幾個概念,有效地將它們隱藏在外部用戶之外,并暴露了其內(nèi)容的簡化視圖。引入模塊將復(fù)雜的系統(tǒng)分成了幾個通常更簡單的部分。

圖2:將系統(tǒng)劃分為模塊,突出顯示有界上下文
該圖有幾個值得注意的地方:
- 模塊為其 公共API 創(chuàng)建了新的概念。
 - API 入口點增加了 擁有者模塊 和其客戶的復(fù)雜性。
 - 系統(tǒng)中的概念總數(shù)已增加(從18個到22個),但系統(tǒng)中最高復(fù)雜度已下降(從18到15)。
 
在這里,我們看到引入模塊如何將分而治之的方法應(yīng)用于減少在系統(tǒng)的任何部分上工作時的認知負荷,以較小的總工作量為代價。
在我們的電話簿示例中,與地區(qū)相關(guān)的字符串比較和聯(lián)系人姓名的字母排序的特殊性(包括大小寫敏感性)應(yīng)該更好地保留在一個簡單的字符串比較接口之后,以解除電話簿引擎程序員對支持外語的復(fù)雜性的負擔(dān)。
模塊代表 有界上下文 [DDD] — 系統(tǒng)知識的領(lǐng)域,這些領(lǐng)域操作不同的術(shù)語集。對于電話簿來說,整理 和 大小寫敏感性 對于電話簿引擎并不重要 — 它們只在語言支持的上下文中定義。另一方面,通過號碼匹配聯(lián)系人 在語言支持模塊中并未定義 — 該術(shù)語僅存在于電話簿引擎中。程序員所面臨的是當前有界上下文的復(fù)雜性。
除了將問題分解為較簡單的子問題外,模塊還帶來了一些額外的好處:
- 代碼重用。一個良好編寫的模塊可以在多個項目中使用。
 - 勞動分工。一旦系統(tǒng)被拆分為模塊,并且每個模塊都被分配給一個程序員,開發(fā)就會被高效地并行化。
 - 高級概念。有些情況允許將原始問題的幾個概念合并為更高級的聚合,進一步降低復(fù)雜性:
 

圖3:合并了綠色模塊的兩個API概念
例如,電話簿的原始定義包含 名 和 姓。一旦我們將語言支持分離到一個專用模塊中,我們可能會發(fā)現(xiàn)各種地區(qū)在表示聯(lián)系人時有所不同:一些(美國)使用“名 + 姓”,而其他一些(日本)則需要“姓 + 名”。如果我們想要擺脫這個細節(jié),我們應(yīng)該使用一個新的 全名 概念,它以特定于區(qū)域的方式連接名和姓。這樣的改變實際上簡化了電話簿的某些表示邏輯和代碼,因為它用一個概念替換了兩個概念。
耦合度和內(nèi)聚性
為了有效使用模塊,我們需要學(xué)習(xí)一些新的概念:
- 耦合度 是模塊之間連接數(shù)量(密度)的度量,相對于模塊的大小。
 - 內(nèi)聚性 是模塊內(nèi)連接數(shù)量(密度)的度量,相對于模塊的大小。
 
經(jīng)驗法則是要追求 低耦合和高內(nèi)聚,這意味著每個模塊應(yīng)該封裝一組相關(guān)(密切交互)的概念。這就是我們在圖2和圖3中將系統(tǒng)劃分為的方式?,F(xiàn)在讓我們看看如果我們違反規(guī)則會發(fā)生什么:

圖4:上部模塊耦合度高
拆分一個內(nèi)聚模塊(一組相互交互的概念),會產(chǎn)生兩個強耦合的模塊。這正是我們想要的,只是每個新模塊幾乎與原始模塊一樣復(fù)雜。也就是說,我們現(xiàn)在面臨兩個艱巨的任務(wù),而不是一個。此外,系統(tǒng)的性能可能很差,因為模塊之間的通信很少是最佳的,而我們卻有太多這樣的通信。

圖5:下部模塊內(nèi)聚性低
如果我們將幾個概念集聚在同一個模塊中會發(fā)生什么?對于小模塊來說,不會發(fā)生什么太糟糕的事 — 模塊的復(fù)雜性高于其各個部分,但低于它們的總和。實際上,多個無關(guān)的函數(shù)通常被收集到一個‘utils’或‘tools’文件或目錄中,以減輕 操作復(fù)雜性。
開發(fā)與操作復(fù)雜性
我們上面討論的是 結(jié)構(gòu)性 或 開發(fā)復(fù)雜性 —— 有界上下文內(nèi)部的概念和規(guī)則的數(shù)量。然而,我們還需要理解系統(tǒng)作為一個整體的操作和組件,從而導(dǎo)致 操作 或 集成復(fù)雜性:
- 這個新需求是否適合現(xiàn)有模塊,還是需要一個專用模塊?
 - 我們使用了哪些存在已知安全漏洞的庫?
 - 有沒有辦法減少我們的云服務(wù)成本?
 - 1% 的請求超時了。你能調(diào)查一下嗎?
 - 我的團隊需要實現(xiàn)這個和那個。我們有適合重用的東西嗎?
 - 那個全局變量到底是干什么的?
 - 我們真的需要將這段代碼投入生產(chǎn)嗎?
 - 我需要稍微改變一下那個共享組件的行為。有什么意見嗎?
 
當部署了數(shù)百或數(shù)千個模塊時,沒有人知道答案。這類似于需要在 Linux 下執(zhí)行某些操作的情況:已預(yù)安裝了數(shù)百個工具,數(shù)千個其他工具則作為軟件包可用,但唯一的前進方式是首先在搜索引擎中搜索您的需求,然后嘗試搜索結(jié)果中的兩三種方法,看哪種適合您的設(shè)置。不幸的是,谷歌并不索引您公司的代碼。
模塊的組合
一個模塊不僅可以封裝單個概念,還可以封裝其他模塊。這并不奇怪,因為 OOP 類是一種模塊 — 它具有公共方法和私有成員。將一個模塊隱藏在另一個模塊中會將其從全局范圍中刪除,減少了系統(tǒng)的操作復(fù)雜性 — 現(xiàn)在不再是系統(tǒng)的架構(gòu)師,而是外部模塊的維護者必須記住內(nèi)部模塊。一方面,在組織和代碼中建立了可管理的層次結(jié)構(gòu)。另一方面,代碼重用和許多優(yōu)化幾乎不可能實現(xiàn),因為內(nèi)部模塊在整個組織中幾乎不為人知:

圖6:模塊的組合阻止了重用
如果我們的內(nèi)部模塊的功能被我們的客戶需要,我們有兩個不好的選擇:
轉(zhuǎn)發(fā)和重復(fù)

圖7:轉(zhuǎn)發(fā)內(nèi)部模塊的API
我們可以將我們封裝的模塊的API添加到我們的公共API中,并將其調(diào)用轉(zhuǎn)發(fā)到內(nèi)部模塊。然而,這會增加我們模塊的復(fù)雜性,并降低我們模塊的內(nèi)聚性 — 現(xiàn)在我們模塊的每個客戶都暴露于我們封裝的模塊的方法的細節(jié)中,即使他們并不打算使用它。

圖8:重復(fù)內(nèi)部模塊
另一個不好的選擇是讓需要我們封裝的模塊的客戶復(fù)制它并擁有副本作為自己的子模塊。這使我們擺脫了任何共同的責(zé)任,讓我們可以任意修改和誤用我們的內(nèi)部,并違反了常識的一對 規(guī)則。
這兩種方法,即將所有模塊保留在全局范圍內(nèi)和通過組合封裝實用模塊,都在歷史上找到了它們的位置[FSA]。面向服務(wù)的架構(gòu) 基于重用的想法,但卻成了其 企業(yè)服務(wù)總線 的復(fù)雜性的犧牲品,該總線必須考慮系統(tǒng)中的所有交互(API 方法)。作為反應(yīng),微服務(wù) 方法把潮流轉(zhuǎn)向了相反的方向:其支持者不允許在服務(wù)之間共享任何資源或代碼,以確保它們的解耦。















 
 
 




 
 
 
 