成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go工程化(一) 架構(gòu)整潔之道閱讀筆記

開發(fā) 架構(gòu)
本系列為極客時(shí)間 Go 進(jìn)階訓(xùn)練營(yíng)筆記,全文接近 2W 字,篇幅較長(zhǎng),采用書中重點(diǎn)摘錄+不成熟的個(gè)人小結(jié)組成,桌面端可以點(diǎn)擊右側(cè)目錄快速定位到你感興趣的章節(jié)。

 [[388570]]

本系列為極客時(shí)間 Go 進(jìn)階訓(xùn)練營(yíng)筆記,同步直播更新,預(yù)計(jì)一周更新 1 ~ 2 篇文章,到 202103 月更新完成

其實(shí)這一篇文章不應(yīng)該算在這里面,(PS: 毛老師課程上沒講這本書)但是恰好最近把這本書讀完了,并且部門內(nèi)推薦大家讀這本書,毛老師在課上也推薦這本書,也和我們這次的主題有一些關(guān)系,一切都是最好的安排,那就放這系列吧。

閱讀建議: 全文接近 2W 字,篇幅較長(zhǎng),采用書中重點(diǎn)摘錄+不成熟的個(gè)人小結(jié)組成,桌面端可以點(diǎn)擊右側(cè)目錄快速定位到你感興趣的章節(jié)

讀書筆記

前言

  • 今天的軟件與過去的軟件本質(zhì)上仍然是一樣的。都是由 if 語句、賦值語句以及 while 循環(huán)組成的
  • 軟件架構(gòu)的規(guī)則其實(shí)就是排列組合代碼塊的規(guī)則

這說明什么呢,說明了可能我們以為過時(shí)的,古老的技術(shù)或者解決方案也是有用的

第一部分 概述

第 1 章 設(shè)計(jì)與架構(gòu)究竟是什么

  • 架構(gòu)圖里實(shí)際上包含了所有的「底層設(shè)計(jì)細(xì)節(jié)」,這些細(xì)節(jié)信息共同支撐了頂層的架構(gòu)設(shè)計(jì),「底層設(shè)計(jì)信息和頂層架構(gòu)設(shè)計(jì)」共同組成了整個(gè)房屋的架構(gòu)文檔。
  • 「軟件架構(gòu)的終極目標(biāo)是,用最小的人力成本來滿足構(gòu)建和維護(hù)該系統(tǒng)的需求。」
  • 一個(gè)軟件架構(gòu)的優(yōu)劣,「可以用它滿足用戶需求所需要的成本來衡量?!?/li>
  • 亂麻系統(tǒng):這種系統(tǒng)一般都是沒有經(jīng)過設(shè)計(jì),匆匆忙忙被構(gòu)建起來的
  1. 我們經(jīng)常使用一句話來欺騙自己**“我們可以未來再重構(gòu)代碼,產(chǎn)品上線最重要!”**
  2. 另外一個(gè)錯(cuò)誤的觀點(diǎn):「“在工程中容忍糟糕的代碼存在可以在短期內(nèi)加快該工程上線的速度,未來這些代碼會(huì)造成一些額外的工作量,但是并沒有什么大不了”」
  • 研發(fā)團(tuán)隊(duì)最好的選擇是清晰地認(rèn)識(shí)并避開工程師們過度自信的特點(diǎn),開始認(rèn)真地對(duì)待自己的代碼架構(gòu),對(duì)其質(zhì)量負(fù)責(zé)

軟件的架構(gòu)的終極目標(biāo),以及如何衡量一個(gè)架構(gòu)的優(yōu)劣,尤其是兩個(gè)錯(cuò)誤的觀點(diǎn)非常感同身受,我也說過類似的話語,還有一句話是“當(dāng)前的需求非常緊急,這只是一個(gè)臨時(shí)的系統(tǒng)很快就會(huì)被替換掉,我們先完成它”。作為一個(gè)專業(yè)的技術(shù)人員我們需要有一些底線來保證我們的代碼架構(gòu)和質(zhì)量,不能輕易妥協(xié),這在 Bob 大叔整潔系列的另外一本書中也有提到。

第 2 章 兩個(gè)價(jià)值緯度

1.行為價(jià)值

只有可以產(chǎn)生收入的代碼才是有用的代碼,技術(shù)是需要為業(yè)務(wù)服務(wù)的,但是我們的工作并不是說就按照需求文檔寫代碼,修bug就行了

  • 軟件系統(tǒng)的行為是其最直觀的價(jià)值維度。程序員的工作就是讓機(jī)器按照某種指定方式運(yùn)轉(zhuǎn),給系統(tǒng)的使用者創(chuàng)造或者提高利潤(rùn)。
  • 按照需求文檔編寫代碼,并且修復(fù)任何 Bug。這真是大錯(cuò)特錯(cuò)。
  • 「系統(tǒng)行為,是緊急的,但是并不總是特別重要。」

2.架構(gòu)價(jià)值

架構(gòu)價(jià)值主要就是為了能夠應(yīng)對(duì)變化,其實(shí)舉個(gè)反面例子,我們之前有一個(gè)系統(tǒng) A 是直接在 A 中調(diào)用接口獲取數(shù)據(jù),隨著業(yè)務(wù)的發(fā)展我們拆分了一個(gè)應(yīng)用 B 需要從 B 中獲取對(duì)應(yīng)的數(shù)據(jù),這個(gè)時(shí)候我們發(fā)現(xiàn)代碼變更非常嚴(yán)重,從里到外都需要進(jìn)行重構(gòu)修改,這就是典型了依賴了“具體的形狀”導(dǎo)致的額外成本

  • 為了達(dá)到軟件的本來目的,軟件系統(tǒng)必須夠“軟”——也就是說,軟件應(yīng)該容易被修改。
  • 當(dāng)需求方改變需求的時(shí)候,隨之所需的軟件變更必須可以簡(jiǎn)單而方便地實(shí)現(xiàn)。
  • 變更實(shí)施的難度應(yīng)該和變更的范疇(scope)成等比關(guān)系,而與變更的具體形狀(shape)無關(guān)。
  • 「系統(tǒng)架構(gòu),是重要的,但是并不總是特別緊急?!?/li>

3.重要緊急的排序

  • 重要且緊急
  • 重要不緊急
  • 不重要但緊急
  • 不重要且不緊急

4.業(yè)務(wù)/市場(chǎng)的同事往往是無法評(píng)估架構(gòu)的重要性的,所以,「平衡系統(tǒng)架構(gòu)的重要性與功能的緊急程度這件事,是軟件研發(fā)人員自己的職責(zé)?!?/p>

我們當(dāng)前處在公共技術(shù)的部門,這也是一個(gè)經(jīng)常困擾的一個(gè)例子,所有的業(yè)務(wù)方在提需求的時(shí)候都會(huì)表示需求非常緊急,但是這個(gè)功能的實(shí)現(xiàn)對(duì)我們來說重要嗎?這個(gè)需要打上一個(gè)大大的問號(hào),其他部門的同學(xué)其實(shí)是無法對(duì)評(píng)估需求對(duì)于我們的重要性的,這個(gè)需要我們自己來權(quán)衡。

5.為好的軟件架構(gòu)而持續(xù)斗爭(zhēng)

這不僅僅是架構(gòu)師的職責(zé),這是每一位開發(fā)同學(xué)的職責(zé),忽略架構(gòu)的價(jià)值會(huì)導(dǎo)致我們帶來無休止的加班,領(lǐng)導(dǎo)的質(zhì)疑,產(chǎn)品的argue

  • 軟件架構(gòu)師這一職責(zé)本身就應(yīng)更關(guān)注系統(tǒng)的整體結(jié)構(gòu),而不是具體的功能和系統(tǒng)行為的實(shí)現(xiàn)。
  • 「軟件架構(gòu)師必須創(chuàng)建出一個(gè)可以讓功能實(shí)現(xiàn)起來更容易、修改起來更簡(jiǎn)單、擴(kuò)展起來更輕松的軟件架構(gòu)。」
  • 如果忽視軟件架構(gòu)的價(jià)值,系統(tǒng)將會(huì)變得越來越難以維護(hù),終會(huì)有一天,系統(tǒng)將會(huì)變得再也無法修改。

第二部分 從基礎(chǔ)構(gòu)件開始:編程范式

編程范式指的是程序的編寫模式,與具體的編程語言關(guān)系相對(duì)較小。這些范式會(huì)告訴你應(yīng)該在什么時(shí)候采用什么樣的代碼結(jié)構(gòu) 當(dāng)前的三種編程范式,結(jié)構(gòu)化編程,面向?qū)ο?,函?shù)式編程

第 3 章 編程范式總覽

1.結(jié)構(gòu)化編程(面向過程)

  • 結(jié)構(gòu)化編程對(duì)程序控制權(quán)的直接轉(zhuǎn)移進(jìn)行了限制和規(guī)范。
  • 限制了 goto 語句的使用

2.面向?qū)ο?/p>

  • 面向?qū)ο缶幊虒?duì)程序控制權(quán)的間接轉(zhuǎn)移進(jìn)行了限制和規(guī)范。
  • 限制了函數(shù)指針的使用

3.函數(shù)式編程

這個(gè)角度之前還沒有看到過,對(duì)我而言還是比較新奇,從限制的角度來看不同的編程范式有著不同限制,可以減少在編程當(dāng)中出錯(cuò)的可能

  • 函數(shù)式編程對(duì)程序中的賦值進(jìn)行了限制和規(guī)范。
  • 限制了賦值語句的使用

第 4 章 結(jié)構(gòu)化編程

  • Bohm 和 Jocopini 剛剛證明了人們可以用順序結(jié)構(gòu)、分支結(jié)構(gòu)、循環(huán)結(jié)構(gòu)這三種結(jié)構(gòu)構(gòu)造出任何程序。
  • 證明了我們構(gòu)建可推導(dǎo)模塊所需要的控制結(jié)構(gòu)集與構(gòu)建所有程序所需的控制結(jié)構(gòu)集的最小集是等同的。
  • 結(jié)構(gòu)化編程范式可將模塊遞歸降解拆分為可推導(dǎo)的單元,這就意味著模塊也可以按功能進(jìn)行降解拆分。
  • 測(cè)試只能展示 Bug 的存在,并不能證明不存在 Bug。

結(jié)構(gòu)化編程可以讓我們將一個(gè)大的模塊按照功能進(jìn)行拆分,變成小的功能模塊,同時(shí)通過測(cè)試我們可以證明其錯(cuò)誤性,無論是架構(gòu)上還是實(shí)際的開發(fā)過程中,大模塊拆小模塊的思路的數(shù)不勝數(shù),其實(shí)單體應(yīng)用拆分為微服務(wù)應(yīng)用也是這個(gè)范疇內(nèi)的。

  • 換句話說,一段程序可以由一個(gè)測(cè)試來證明其錯(cuò)誤性,但是卻不能被證明是正確的。測(cè)試的作用是讓我們得出某段程序已經(jīng)足夠?qū)崿F(xiàn)當(dāng)前目標(biāo)這一結(jié)論。

第 5 章 面向?qū)ο缶幊?/h3>

1.什么是面向?qū)ο?

面向?qū)ο罄碚撌窃?1966 年提出的,當(dāng)時(shí) Dahl 和 Nygaard 主要是將函數(shù)調(diào)用棧遷移到了堆區(qū)域中

  • 一種常見的回答是“數(shù)據(jù)與函數(shù)的組合”,這種不太貼切
  • 另一種常見的回答是“面向?qū)ο缶幊淌且环N對(duì)真實(shí)世界進(jìn)行建模的方式”,這有點(diǎn)避重就輕
  • 面向?qū)ο缶幊淌欠庋b(encapsulation)、繼承(inheritance)、多態(tài)(polymorphism)這三項(xiàng)的有機(jī)組合

2.封裝

  • 通過采用封裝特性,我們可以把一組相關(guān)聯(lián)的數(shù)據(jù)和函數(shù)圈起來,使圈外面的代碼只能看見部分函數(shù),數(shù)據(jù)則完全不可見
  • C 語言也支持完整的封裝特性,使用 C 語言的時(shí)候應(yīng)用頭文件 .h 的模塊是無法知道結(jié)構(gòu)體中的成員變量的,但是 C++ 的頭文件中包含了成員信息。
  • 不是面向?qū)ο笳Z言的 C 語言相對(duì)于面向?qū)ο笳Z言 C++ 反而擁有更好的封裝特性,所以「我們很難說強(qiáng)封裝是面向?qū)ο缶幊痰谋匾獥l件」

3.繼承

  • 繼承的主要作用是讓我們可以在某個(gè)作用域內(nèi)對(duì)外部定義的某一組變量與函數(shù)進(jìn)行覆蓋
  • C 其實(shí)也可以實(shí)現(xiàn)繼承,只是相對(duì)面向?qū)ο笳Z言而言會(huì)更加困難。

4.多態(tài)

  • 歸根結(jié)底,多態(tài)其實(shí)不過就是函數(shù)指針的一種應(yīng)用。但是函數(shù)指針非常危險(xiǎn),需要人為的遵守很多約定,容易出 bug。
  • 面向?qū)ο缶幊陶Z言雖然在多態(tài)上并沒有理論創(chuàng)新,但它們也確實(shí)讓多態(tài)變得更安全、更便于使用了。

5.依賴反轉(zhuǎn)

  • 依賴關(guān)系(或者叫繼承關(guān)系)的方向和控制流正好是相反的,我們稱之為依賴反轉(zhuǎn)
  • 依賴關(guān)系都可以通過引入接口的方式來進(jìn)行反轉(zhuǎn)。
  • 通過這種方法,軟件架構(gòu)師可以完全控制采用了面向?qū)ο筮@種編程方式的系統(tǒng)中所有的源代碼依賴關(guān)系,
  • 而不再受到系統(tǒng)控制流的限制。不管哪個(gè)模塊調(diào)用或者被調(diào)用,軟件架構(gòu)師都可以隨意更改源代碼依賴關(guān)系。
  • 當(dāng)某個(gè)組件的源代碼需要修改時(shí),僅僅需要重新部署該組件,不需要更改其他組件,這就是獨(dú)立部署能力。

6**面向?qū)ο缶幊叹褪且远鄳B(tài)為手段來對(duì)源代碼中的依賴關(guān)系進(jìn)行控制的能力,**這種能力讓軟件架構(gòu)師可以構(gòu)建出某種插件式架構(gòu),讓高層策略性組件與底層實(shí)現(xiàn)性組件相分離,底層組件可以被編譯成插件,實(shí)現(xiàn)獨(dú)立于高層組件的開發(fā)和部署。

在剛學(xué)習(xí)編程的時(shí)候,學(xué)到面向?qū)ο笠欢〞?huì)說到,封裝、繼承、和多態(tài),但是通過這一章我們可以發(fā)現(xiàn),面向?qū)ο笳Z言的封裝不一定比面向過程的 C 語言做的更好,這里強(qiáng)調(diào)的更重要的是使用多態(tài)的手段對(duì)源碼的依賴關(guān)系進(jìn)行控制,主要是指通過接口來實(shí)現(xiàn)依賴反轉(zhuǎn),這樣就可以將組件進(jìn)行分離,可以進(jìn)行獨(dú)立開發(fā)和部署。我現(xiàn)在主要使用的語言是 Go,有一個(gè)常見的問題就是 Go 是不是一個(gè)面向?qū)ο笳Z言,回答也是 Yes or no,是也不是,Go 不支持繼承,也不支持函數(shù)重載,運(yùn)算符重載等在面向?qū)ο笳Z言非常常見的特性,但是 Go 的接口非常強(qiáng)大,不需要顯示依賴接口的設(shè)計(jì)讓我們?cè)谝蕾嚪崔D(zhuǎn)的使用上更加游刃有余。

第 6 章 函數(shù)式編程

  • 函數(shù)式編程語言中的變量(Variable)是不可變(Vary)的。
  • 為什么軟件架構(gòu)師要操心變量的可變性呢?答案顯而易見:所有的競(jìng)爭(zhēng)問題、死鎖問題、并發(fā)更新問題都是由可變變量導(dǎo)致的。
  • 一個(gè)架構(gòu)設(shè)計(jì)良好的應(yīng)用程序應(yīng)該將狀態(tài)修改的部分和不需要修改狀態(tài)的部分隔離成單獨(dú)的組件,然后用合適的機(jī)制來保護(hù)可變量。
  • 事件溯源體系下,我們只存儲(chǔ)事務(wù)記錄,不存儲(chǔ)具體狀態(tài)。當(dāng)需要具體狀態(tài)時(shí),我們只要從頭開始計(jì)算所有的事務(wù)即可。
  • 這種數(shù)據(jù)存儲(chǔ)模式中不存在刪除和更新的情況,我們的應(yīng)用程序不是 CRUD,而是 CR。因?yàn)楦潞蛣h除這兩種操作都不存在了,自然也就不存在并發(fā)問題。

在我們剛剛結(jié)束的上一個(gè)系列,[Go并發(fā)編程](https://lailin.xyz/categories/Go%E8%BF%9B%E9%98%B6%E8%AE%AD%E7%BB%83%E8%90%A5/Go%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/)中,我們講到的大量手段來避免數(shù)據(jù)競(jìng)爭(zhēng),這些都是由于在并發(fā)時(shí)寫入導(dǎo)致的,而函數(shù)式編程最重要的一個(gè)特性就是變量不可變,由于變量無法被修改所以自然而然就不存在數(shù)據(jù)競(jìng)爭(zhēng),也就不需要加鎖,這樣可以獲得很高的性能。

第三部分 設(shè)計(jì)原則

軟件構(gòu)建中層結(jié)構(gòu)的主要目標(biāo):

  • 使軟件可容忍被改動(dòng)。
  • 使軟件更容易被理解。
  • 構(gòu)建可在多個(gè)軟件系統(tǒng)中復(fù)用的組件。

在之前的[《Go設(shè)計(jì)模式》](https://lailin.xyz/post/go-design-pattern.html)系列文章當(dāng)中也有提到 SOLID 原則,換個(gè)角度可以發(fā)現(xiàn)這些其實(shí)都是殊途同歸的一些東西,SOLID 原則的歷史已經(jīng)非常悠久了,但是直到現(xiàn)在它仍然非常具有指導(dǎo)意義。

第 7 章 SRP:?jiǎn)我宦氊?zé)原則

1.「任何一個(gè)軟件模塊都應(yīng)該有且僅有一個(gè)被修改的原因?!?/p>

2.任何一個(gè)軟件模塊都應(yīng)該只對(duì)一個(gè)用戶(User)或系統(tǒng)利益相關(guān)者(Stakeholder)負(fù)責(zé)。

3.「任何一個(gè)軟件模塊都應(yīng)該只對(duì)某一類行為者負(fù)責(zé)?!?/p>

4.「反例: 代碼合并沖突」

單一職責(zé)原則非常容易被誤認(rèn)為“每個(gè)模塊應(yīng)該只做一件事”,沒錯(cuò)之前我也是這么理解的,雖然這個(gè)描述沒錯(cuò),但是這并不是 SRP 的全部。

  • 多人為了不同的目的修改了同一份源代碼,這很容易造成問題的產(chǎn)生。
  • 避免這種問題產(chǎn)生的方法就是將服務(wù)不同行為者的代碼進(jìn)行切分。

第 8 章 OCP:開閉原則

1.設(shè)計(jì)良好的計(jì)算機(jī)軟件應(yīng)該易于擴(kuò)展,同時(shí)抗拒修改。

  • 換句話說,「一個(gè)設(shè)計(jì)良好的計(jì)算機(jī)系統(tǒng)應(yīng)該在不需要修改的前提下就可以輕易被擴(kuò)展?!?/li>

2.一個(gè)好的軟件架構(gòu)設(shè)計(jì)師會(huì)努力將舊代碼的修改需求量降至最小,甚至為 0。

  • 可以先將滿足不同需求的代碼分組(即 SRP),然后再來調(diào)整這些分組之間的依賴關(guān)系(即 DIP)

3.如果 A 組件不想被 B 組件上發(fā)生的修改所影響,那么就應(yīng)該讓 B 組件依賴于 A 組件。

4.軟件架構(gòu)師可以根據(jù)相關(guān)函數(shù)被修改的原因、修改的方式及修改的時(shí)間來對(duì)其進(jìn)行分組隔離,并將這些互相隔離的函數(shù)分組整理成組件結(jié)構(gòu),使得高階組件不會(huì)因低階組件被修改而受到影響。

5.OCP 是我們進(jìn)行系統(tǒng)架構(gòu)設(shè)計(jì)的主導(dǎo)原則,其主要目標(biāo)是讓系統(tǒng)易于擴(kuò)展,同時(shí)限制其每次被修改所影響的范圍。

開閉原則在架構(gòu)設(shè)計(jì)上非常常見,其中最常見的做法就是使用接口實(shí)現(xiàn)依賴反轉(zhuǎn),如果開閉原則實(shí)現(xiàn)的不好就有可能導(dǎo)致我們?cè)谶M(jìn)行后續(xù)功能擴(kuò)展的時(shí)候牽一發(fā)而動(dòng)全身,成本非常的高。

第 9 章 LSP:里氏替換原則

1.如果對(duì)于每個(gè)類型是 S 的對(duì)象 o1 都存在一個(gè)類型為 T 的對(duì)象 o2,能使操作 T 類型的程序 P 在用 o2 替換 o1 時(shí)行為保持不變,我們就可以將 S 稱為 T 的子類型。

2.比較常見的一個(gè)違反 LSP 原則的例子,長(zhǎng)方形與正方形

這個(gè)反面例子對(duì)我的震撼比較大,依稀記得最開始在學(xué)習(xí)編程語言繼承的例子的時(shí)候就常常用長(zhǎng)方形正方形來舉例,但是這個(gè)其實(shí)是違反了里式替換原則的。在架構(gòu)設(shè)計(jì)上這個(gè)原則也十分的重要,因?yàn)槲覀冎挥凶龅搅? LSP 我們才可以在例如數(shù)據(jù)庫(kù)類型切換,微服務(wù)拆分這種場(chǎng)景下做的游刃有余。

  • Square 類并不是 Rectangle 類的子類型,因?yàn)?Rectangle 類的高和寬可以分別修改,而 Square 類的高和寬則必須一同修改。

第 10 章 ISP:接口隔離原則

  • ISP 最初的成因:在一般情況下,任何層次的軟件設(shè)計(jì)如果依賴于不需要的東西,都會(huì)是有害的。
  • 任何層次的軟件設(shè)計(jì)如果依賴了它并不需要的東西,就會(huì)帶來意料之外的麻煩。

由于 Go 接口的隱式依賴的特性,讓 ISP 在 Go 中處處可見,我們常常采用的方式就是在調(diào)用者處依賴接口,而不管實(shí)現(xiàn),這樣就可以做到,模塊分離以及最小化依賴。

第 11 章 DIP:依賴反轉(zhuǎn)原則

1.如果想要設(shè)計(jì)一個(gè)靈活的系統(tǒng),在源代碼層次的依賴關(guān)系中就應(yīng)該多引用抽象類型,而非具體實(shí)現(xiàn)。

2.在應(yīng)用 DIP 時(shí),我們也不必考慮穩(wěn)定的操作系統(tǒng)或者平臺(tái)設(shè)施,因?yàn)檫@些系統(tǒng)接口很少會(huì)有變動(dòng)。

3.主要應(yīng)該關(guān)注的是軟件系統(tǒng)內(nèi)部那些會(huì)經(jīng)常變動(dòng)的(volatile)具體實(shí)現(xiàn)模塊,這些模塊是不停開發(fā)的,也就會(huì)經(jīng)常出現(xiàn)變更。

4.編碼規(guī)范

通常來說,接口會(huì)比實(shí)現(xiàn)更加穩(wěn)定,舉個(gè)反例,如果接口變動(dòng)實(shí)現(xiàn)是必須要跟著修改的,因?yàn)閷?shí)現(xiàn)是依賴接口的,但是反過來確未必。DIP 原則指導(dǎo)我們無論是在架構(gòu)設(shè)計(jì)還是在編碼實(shí)現(xiàn)當(dāng)中都應(yīng)該盡量的依賴抽象而不是實(shí)現(xiàn)細(xì)節(jié)。

  • 應(yīng)在代碼中多使用抽象接口,盡量避免使用那些多變的具體實(shí)現(xiàn)類。
  • 不要在具體實(shí)現(xiàn)類上創(chuàng)建衍生類。我們對(duì)繼承的使用應(yīng)該格外小心。即使是在稍微便于修改的動(dòng)態(tài)類型語言中,這條守則也應(yīng)該被認(rèn)真考慮
  • 不要覆蓋(override)包含具體實(shí)現(xiàn)的函數(shù)
  • 應(yīng)避免在代碼中寫入與任何具體實(shí)現(xiàn)相關(guān)的名字,或者是其他容易變動(dòng)的事物的名字。

第四部分 組件構(gòu)建原則

第 12 章 組件

1.組件是軟件的部署單元,是整個(gè)軟件系統(tǒng)在部署過程中可以獨(dú)立完成部署的最小實(shí)體

  • 例如:.jar, .gem, .dll 文件

2.鏈接加載器讓程序員們可以將程序切分成多個(gè)可被分別編譯、加載的程序段

3.組件化的插件式架構(gòu)已經(jīng)成為我們習(xí)以為常的軟件構(gòu)建形式了。

第 13 章 組件聚合

1.構(gòu)建組件相關(guān)的基本原則

  • REP:復(fù)用/發(fā)布等同原則
  • CCP:共同閉包原則
  • CRP:共同復(fù)用原則

2.REP:復(fù)用/發(fā)布等同原則

  • 軟件復(fù)用的最小粒度應(yīng)等同于其發(fā)布的最小粒度。
  • REP 原則就是指組件中的類與模塊必須是彼此緊密相關(guān)的
  • 一個(gè)組件不能由一組毫無關(guān)聯(lián)的類和模塊組成,它們之間應(yīng)該有一個(gè)共同的主題或者大方向。

3.CCP:共同閉包原則

  • 我們應(yīng)該將那些會(huì)同時(shí)修改,并且為相同目的而修改的類放到同一個(gè)組件中,而將不會(huì)同時(shí)修改,并且不會(huì)為了相同目的而修改的那些類放到不同的組件中。

4.CRP:共同復(fù)用原則

  • 不要強(qiáng)迫一個(gè)組件的用戶依賴他們不需要的東西。
  • 不要依賴不需要用到的東西。

5.組件張力圖

 

image.png

看到這三個(gè)原則會(huì)感到有點(diǎn)熟悉,像共同閉包原則就和 SOLID 中的單一職責(zé)原則類似,共同復(fù)用原則和接口隔離原則看上去也有那么幾分相似,這些知識(shí)從不同的角度看待總結(jié)問題的不同術(shù)語。最后這個(gè)組件張力圖很有意思,這說明我們?cè)谶M(jìn)行架構(gòu)設(shè)計(jì)的時(shí)候是不可能做到每一項(xiàng)都很完美的,這當(dāng)中會(huì)有一個(gè)取舍的過程,書中講到,一般而言會(huì)項(xiàng)目初期會(huì)從三角右側(cè)開始,進(jìn)行一段時(shí)間后會(huì)滑動(dòng)到左邊,是因?yàn)樵诔跗跒榱诵饰覀兛梢誀奚欢ǖ膹?fù)用性,但是隨著依賴關(guān)系越來越復(fù)雜,那么我們就要考慮復(fù)用和擴(kuò)展了。

第 14 章 組件耦合

  • 組件依賴關(guān)系圖中不應(yīng)該出現(xiàn)環(huán)。
  • 當(dāng)組件結(jié)構(gòu)依賴圖中存在循環(huán)依賴時(shí),想要按正確的順序構(gòu)建組件幾乎是不可能的。
  • 打破循環(huán)依賴
  1. 應(yīng)用依賴反轉(zhuǎn)原則(DIP)
  2. 創(chuàng)建一個(gè)新的組件,并讓 Entities 與 Authorize 這兩個(gè)組件都依賴于它。將現(xiàn)有的這兩個(gè)組件中互相依賴的類全部放入新組件
  • 組件結(jié)構(gòu)圖是不可能自上而下被設(shè)計(jì)出來的。它必須隨著軟件系統(tǒng)的變化而變化和擴(kuò)張,而不可能在系統(tǒng)構(gòu)建的最初就被完美設(shè)計(jì)出來。
  • 組件依賴結(jié)構(gòu)圖并不是用來描述應(yīng)用程序功能的,它更像是應(yīng)用程序在構(gòu)建性與維護(hù)性方面的一張地圖
  • 組件結(jié)構(gòu)圖中的一個(gè)重要目標(biāo)是指導(dǎo)如何隔離頻繁的變更
  • 如果我們?cè)谠O(shè)計(jì)具體類之前就來設(shè)計(jì)組件依賴關(guān)系,那么幾乎是必然要失敗的。因?yàn)樵诋?dāng)下,我們對(duì)項(xiàng)目中的共同閉包一無所知,也不可能知道哪些組件可以復(fù)用,這樣幾乎一定會(huì)創(chuàng)造出循環(huán)依賴的組件。

在 Go 中在編譯器上就限制了我們不能出現(xiàn)循環(huán)依賴,所以我們大量的使用了 DIP 的方式,但是講層次拔高一點(diǎn),從微服務(wù)的角度來講仍然不應(yīng)該出現(xiàn)循環(huán)依賴,如果出現(xiàn)那么在版本發(fā)布的時(shí)候可能會(huì)導(dǎo)致災(zāi)難性的后果,架構(gòu)的原則都是想通的,我們要時(shí)刻警惕循環(huán)依賴的出現(xiàn),對(duì)于微服務(wù)來說可以在 api 網(wǎng)關(guān)進(jìn)行判定是否成環(huán)

穩(wěn)定依賴原則

  • 依賴關(guān)系必須要指向更穩(wěn)定的方向
  • 任何一個(gè)我們預(yù)期會(huì)經(jīng)常變更的組件都不應(yīng)該被一個(gè)難于修改的組件所依賴,否則這個(gè)多變的組件也將會(huì)變得非常難以被修改
  • 讓軟件組件難于修改的一個(gè)最直接的辦法就是讓很多其他組件依賴于它。

穩(wěn)定性指標(biāo)

這一部分提出了一個(gè)對(duì)我現(xiàn)階段非常有用的一個(gè)原則,被大量依賴的組件應(yīng)該是穩(wěn)定的,依賴關(guān)系必須要指向更穩(wěn)定的方向,我當(dāng)前處在公共技術(shù)團(tuán)隊(duì),我們的服務(wù)被外部大量的依賴,所以在變更的時(shí)候會(huì)非常的麻煩,我們 I 值非常的小,幾乎可以說接近于 0,所以我們的服務(wù)在設(shè)計(jì)時(shí)一定要滿足開閉原則,保證足夠的擴(kuò)展性。

  • Fan-in:入向依賴,這個(gè)指標(biāo)指代了組件外部類依賴于組件內(nèi)部類的數(shù)量。
  • Fan-out:出向依賴,這個(gè)指標(biāo)指代了組件內(nèi)部類依賴于組件外部類的數(shù)量。
  • I:不穩(wěn)定性,I=Fan-out/(Fan-in+Fan-out)。該指標(biāo)的范圍是[0,1],I=0 意味著組件是最穩(wěn)定的,I=1 意味著組件是最不穩(wěn)定的。
  1. 其中一種方法是計(jì)算所有入和出的依賴關(guān)系。通過這種方法,我們就可以計(jì)算出一個(gè)組件的位置穩(wěn)定性(positionalstability)。
  2. 穩(wěn)定依賴原則(SDP)的要求是讓每個(gè)組件的 I 指標(biāo)都必須大于其所依賴組件的 I 指標(biāo)。也就是說,組件結(jié)構(gòu)依賴圖中各組件的 I 指標(biāo)必須要按其依賴關(guān)系方向遞減。

穩(wěn)定抽象原則

穩(wěn)定抽象原則說明了越穩(wěn)定的組件應(yīng)該越抽象,從代碼的角度來講,接口是最抽象的組件之一,因?yàn)榻涌谝话悴粫?huì)有其他外部的依賴,而被大量依賴,同時(shí)還給出一個(gè)統(tǒng)計(jì)抽象程度的方法,這個(gè)可以用來統(tǒng)計(jì)一下我們現(xiàn)在的現(xiàn)狀。

  • 只有多變的軟件組件落在痛苦區(qū)中才會(huì)造成麻煩
  • 現(xiàn)在我們來看看靠近(1,1)這一位置點(diǎn)的組件。該位置上的組件不會(huì)是我們想要的,因?yàn)檫@些組件通常是無限抽象的,但是沒有被其他組件依賴,這樣的組件往往無法使用。
  • 追求讓這些組件位于主序列線上,或者貼近這條線即可。
  • Nc:組件中類的數(shù)量。
  • Na:組件中抽象類和接口的數(shù)量。
  • A:抽象程度,A=Na÷Nc
  • A 指標(biāo)的取值范圍是從 0 到 1,值為 0 代表組件中沒有任何抽象類,值為 1 就意味著組件中只有抽象類。

一個(gè)組件的抽象化程度應(yīng)該與其穩(wěn)定性保持一致。

如何才能讓一個(gè)無限穩(wěn)定的組件(I=0)接受變更呢?開閉原則(OCP)為我們提供了答案。這個(gè)原則告訴我們:創(chuàng)造一個(gè)足夠靈活、能夠被擴(kuò)展,而且不需要修改的類是可能的,而這正是我們所需

假設(shè) A 指標(biāo)是對(duì)組件抽象化程度的一個(gè)衡量,它的值是組件中抽象類與接口所占的比例。那么:

image.png
  • D 指標(biāo)[8]:距離 D=|A+I-1|,該指標(biāo)的取值范圍是[0,1]。值為 0 意味著組件是直接位于主序列線上的,值為 1 則意味著組件在距離主序列最遠(yuǎn)的位置。
  • 對(duì)于一個(gè)良好的系統(tǒng)設(shè)計(jì)來說,D 指標(biāo)的平均值和方差都應(yīng)該接近于 0

第五部分 軟件架構(gòu)

第 15 章 什么是軟件架構(gòu)

1.軟件架構(gòu)師自身需要是程序員,并且必須一直堅(jiān)持做一線程序員,絕對(duì)不要聽從那些說應(yīng)該讓軟件架構(gòu)師從代碼中解放出來以專心解決高階問題的偽建議

2.如果不親身承受因系統(tǒng)設(shè)計(jì)而帶來的麻煩,就體會(huì)不到設(shè)計(jì)不佳所帶來的痛苦,接著就會(huì)逐漸迷失正確的設(shè)計(jì)方向。

這個(gè)也是常常會(huì)遇到的問題,就現(xiàn)在我能觀察到的為例,架構(gòu)師級(jí)別的基本上沒有看到過再做一線的程序開發(fā)工作,僅僅是平時(shí)的各種管理,規(guī)劃上的事務(wù)就已經(jīng)忙的不可開交,這其實(shí)不僅僅導(dǎo)致了架構(gòu)師本身會(huì)脫節(jié),同時(shí)也會(huì)導(dǎo)致下面的同學(xué)很少有機(jī)會(huì)學(xué)習(xí)到架構(gòu)師們過往的經(jīng)驗(yàn)。

3.軟件架構(gòu)這項(xiàng)工作的實(shí)質(zhì)就是規(guī)劃如何將系統(tǒng)切分成組件,并安排好組件之間的排列關(guān)系,以及組件之間互相通信的方式。

4.設(shè)計(jì)軟件架構(gòu)的目的,就是為了在工作中更好地對(duì)這些組件進(jìn)行研發(fā)、部署、運(yùn)行以及維護(hù)。

5.如果想設(shè)計(jì)一個(gè)便于推進(jìn)各項(xiàng)工作的系統(tǒng),其策略就是要在設(shè)計(jì)中盡可能長(zhǎng)時(shí)間地保留盡可能多的可選項(xiàng)。

6.設(shè)計(jì)良好的架構(gòu)可以讓系統(tǒng)便于理解、易于修改、方便維護(hù),并且能輕松部署?!杠浖軜?gòu)的終極目標(biāo)就是最大化程序員的生產(chǎn)力,同時(shí)最小化系統(tǒng)的總運(yùn)營(yíng)成本?!?/p>

7.開發(fā)

  • 實(shí)現(xiàn)一鍵式的輕松部署應(yīng)該是我們?cè)O(shè)計(jì)軟件架構(gòu)的一個(gè)目標(biāo)

8.運(yùn)行

人力成本往往會(huì)比機(jī)器的成本更高,所以這也就是我們?cè)诖a編寫的過程當(dāng)中對(duì)可讀性和性能需要有一個(gè)權(quán)衡,如果不是差異過大往往代碼的可讀性需要更為重要

  • 幾乎任何運(yùn)行問題都可以通過增加硬件的方式來解決,這避免了軟件架構(gòu)的重新設(shè)計(jì)
  • 基于投入/產(chǎn)出比的考慮,我們的優(yōu)化重心應(yīng)該更傾向于系統(tǒng)的開發(fā)、部署以及維護(hù)
  • 一個(gè)設(shè)計(jì)良好的軟件架構(gòu)應(yīng)該能明確地反映該系統(tǒng)在運(yùn)行時(shí)的需求。

9.維護(hù)

  • 在軟件系統(tǒng)的所有方面中,維護(hù)所需的成本是最高的

10.保持可選項(xiàng)

軟件的高層策略不應(yīng)該關(guān)心其底層到底使用哪一種數(shù)據(jù)庫(kù)

開發(fā)的早期階段也不應(yīng)該選定使用的 Web 服務(wù)

軟件的高層策略壓根不應(yīng)該跟這些有關(guān)。

在開發(fā)的早期階段不應(yīng)過早地采用依賴注入框架

  • 軟件有行為價(jià)值與架構(gòu)價(jià)值兩種價(jià)值。這其中的第二種價(jià)值又比第一種更重要
  • 軟件的靈活性則取決于系統(tǒng)的整體狀況、組件的布置以及組件之間的連接方式。
  • 如果在開發(fā)高層策略時(shí)有意地讓自己擺脫具體細(xì)節(jié)的糾纏,我們就可以將與具體實(shí)現(xiàn)相關(guān)的細(xì)節(jié)決策推遲或延后,因?yàn)樵降巾?xiàng)目的后期,我們就擁有越多的信息來做出合理的決策。
  • 一個(gè)優(yōu)秀的軟件架構(gòu)師應(yīng)該致力于最大化可選項(xiàng)數(shù)量

11.**優(yōu)秀的架構(gòu)師會(huì)小心地將軟件的高層策略與其底層實(shí)現(xiàn)隔離開,讓高層策略與實(shí)現(xiàn)細(xì)節(jié)脫鉤,使其策略部分完全不需要關(guān)心底層細(xì)節(jié),當(dāng)然也不會(huì)對(duì)這些細(xì)節(jié)有任何形式的依賴。**另外,「優(yōu)秀的架構(gòu)師所設(shè)計(jì)的策略應(yīng)該允許系統(tǒng)盡可能地推遲與實(shí)現(xiàn)細(xì)節(jié)相關(guān)的決策,越晚做決策越好」

這一點(diǎn)其實(shí)很容易被忽略掉,因?yàn)槲覀兘?jīng)常做的工作就是細(xì)節(jié)性的工作,在進(jìn)行設(shè)計(jì)的時(shí)候很容易就不自覺的假定 Web UI,MySQL 數(shù)據(jù)庫(kù)這些技術(shù)選型,在這本書的最后一個(gè)章節(jié)還會(huì)講到,這些細(xì)節(jié)。

第 16 章 獨(dú)立性

1.用例

  • 軟件的架構(gòu)必須為其用例提供支持。

2.任何一個(gè)組織在設(shè)計(jì)系統(tǒng)時(shí),往往都會(huì)復(fù)制出一個(gè)與該組織內(nèi)溝通結(jié)構(gòu)相同的系統(tǒng)。

3.一個(gè)設(shè)計(jì)良好的架構(gòu)通常不會(huì)依賴于成堆的腳本與配置文件,也不需要用戶手動(dòng)創(chuàng)建一堆“有嚴(yán)格要求”的目錄與文件

4.如果我們按照變更原因的不同對(duì)系統(tǒng)進(jìn)行解耦,就可以持續(xù)地向系統(tǒng)內(nèi)添加新的用例,而不會(huì)影響舊有的用例。如果我們同時(shí)對(duì)支持這些用例的 UI 和數(shù)據(jù)庫(kù)也進(jìn)行了分組,那么每個(gè)用例使用的就是不同面向的 UI 與數(shù)據(jù)庫(kù),因此增加新用例就更不太可能會(huì)影響舊有的用例了。

5.如果有兩段看起來重復(fù)的代碼,它們走的是不同的演進(jìn)路徑,也就是說它們有著不同的變更速率和變更緣由,那么這兩段代碼就不是真正的重復(fù)

6.解耦模式

“如果兩段看似重復(fù)的代碼,如果有不同的變更速率和原因,那么這兩段代碼就不算是真正的重復(fù)”這有個(gè)非常典型的例子就是 API 接口的參數(shù)和最后我們模型數(shù)據(jù)雖然很多時(shí)候大部分字段是相同的,但是它們的變更速率和原因其實(shí)都是不一樣的,如果把他們耦合在一起雖然前期可能可以減少一些代碼的編寫,但是到最后需要擴(kuò)展時(shí)會(huì)發(fā)現(xiàn)變更會(huì)很困難。之前我還寫了一篇文章 《[Go Web 小技巧(三)Gin 參數(shù)綁定 ](https://lailin.xyz/post/11996.html#2-%E7%94%A8-model-%E5%B1%82%E7%9A%84-struct-%E7%BB%91%E5%AE%9A%E5%8F%82%E6%95%B0)》總結(jié)這種埋坑的技巧 😂

  • 源碼層次:我們可以控制源代碼模塊之間的依賴關(guān)系,以此來實(shí)現(xiàn)一個(gè)模塊的變更不會(huì)導(dǎo)致其他模塊也需要變更或重新編譯
  • 部署層次:我們可以控制部署單元(譬如 jar 文件、DLL、共享庫(kù)等)之間的依賴關(guān)系,以此來實(shí)現(xiàn)一個(gè)模塊的變更不會(huì)導(dǎo)致其他模塊的重新構(gòu)建和部署。
  • 服務(wù)層次:我們可以將組件間的依賴關(guān)系降低到數(shù)據(jù)結(jié)構(gòu)級(jí)別,然后僅通過網(wǎng)絡(luò)數(shù)據(jù)包來進(jìn)行通信。
  • 一個(gè)設(shè)計(jì)良好的架構(gòu)應(yīng)該能允許一個(gè)系統(tǒng)從單體結(jié)構(gòu)開始,以單一文件的形式部署,然后逐漸成長(zhǎng)為一組相互獨(dú)立的可部署單元,甚至是獨(dú)立的服務(wù)或者微服務(wù)。最后還能隨著情況的變化,允許系統(tǒng)逐漸回退到單體結(jié)構(gòu)

第 17 章 劃分邊界

  • 軟件架構(gòu)設(shè)計(jì)本身就是一門劃分邊界的藝術(shù)。
  • 通過劃清邊界,我們可以推遲和延后一些細(xì)節(jié)性的決策,這最終會(huì)為我們節(jié)省大量的時(shí)間、避免大量的問題。
  • I/O 是無關(guān)緊要的
  • GUI 和 BusinessRules 這兩個(gè)組件之間也應(yīng)該有一條邊界線
  • 插件式架構(gòu)的好處
  • 真正核心的是我們業(yè)務(wù)邏輯,而輸入輸出是細(xì)節(jié)

image.png

  • 將系統(tǒng)設(shè)計(jì)為插件式架構(gòu),就等于構(gòu)建起了一面變更無法逾越的防火墻。換句話說,只要 GUI 是以插件形式插入系統(tǒng)的業(yè)務(wù)邏輯中的,那么 GUI 這邊所發(fā)生的變更就不會(huì)影響系統(tǒng)的業(yè)務(wù)邏輯。
  • 邊界線也應(yīng)該沿著系統(tǒng)的變更軸來畫。也就是說,位于邊界線兩側(cè)的組件應(yīng)該以不同原因、不同速率變化著。

第 18 章 邊界剖析

  • 跨邊界調(diào)用指的是邊界線一側(cè)的函數(shù)調(diào)用另一側(cè)的函數(shù),并同時(shí)傳遞數(shù)據(jù)的行為
  • 最簡(jiǎn)單的跨邊界調(diào)用形式,是由低層客戶端來調(diào)用高層服務(wù)函數(shù),這種依賴關(guān)系在運(yùn)行時(shí)和編譯時(shí)會(huì)保持指向一致,都是從低層組件指向高層組件
  • 在單體結(jié)構(gòu)中,組件之間的交互一般情況下都只是普通的函數(shù)調(diào)用,迅速而廉價(jià),這就意味著這種跨源碼層次解耦邊界的通信會(huì)很頻繁
  • 服務(wù)之間的跨邊界通信相對(duì)于函數(shù)調(diào)用來說,速度是非常緩慢的,其往返時(shí)間可以從幾十毫秒到幾秒不等。

不同的邊界的跨邊界調(diào)用的成本是不同的,對(duì)于服務(wù)而言跨服務(wù)調(diào)用的成本非常高,這樣我們?cè)谶M(jìn)行服務(wù)劃分的時(shí)候一定要盡量的內(nèi)聚減少頻繁調(diào)用的情況。

第 19 章 策略與層次

1.策略

  • 本質(zhì)上,所有的軟件系統(tǒng)都是一組策略語句的集合
  • 變更原因、時(shí)間和層次相同的策略應(yīng)該被分到同一個(gè)組件中。反之,變更原因、時(shí)間和層次不同的策略則應(yīng)該分屬于不同的組件
  • 依賴關(guān)系的方向通常取決于它們所關(guān)聯(lián)的組件層次。一般來說,低層組件被設(shè)計(jì)為依賴于高層組件

2.層次

距離 I/O 越遠(yuǎn)的策略層次越高,也就是說我們常見的 Web UI 應(yīng)該屬于最低層次,我們不應(yīng)該依賴 Web UI 這種輸入輸出設(shè)備。同時(shí)給出了組件的劃分原則,變更的時(shí)間原因和層次相同的屬于同一個(gè)組件。

  • 一條策略距離系統(tǒng)的輸入/輸出越遠(yuǎn),它所屬的層次就越高。而直接管理輸入/輸出的策略在系統(tǒng)中的層次是最低的。
  • 數(shù)據(jù)流向和源碼中的依賴關(guān)系并不總處于同一方向上
  • 我們希望源碼中的依賴關(guān)系與其數(shù)據(jù)流向脫鉤,而與組件所在的層次掛鉤。
  • 低層組件應(yīng)該成為高層組件的插件

第 20 章 業(yè)務(wù)邏輯

1.業(yè)務(wù)邏輯就是程序中那些真正用于賺錢或省錢的業(yè)務(wù)邏輯與過程

2.“關(guān)鍵業(yè)務(wù)邏輯”是一項(xiàng)業(yè)務(wù)的關(guān)鍵部分,不管有沒有自動(dòng)化系統(tǒng)來執(zhí)行這項(xiàng)業(yè)務(wù),這一點(diǎn)是不會(huì)改變的。

3.業(yè)務(wù)實(shí)體

  • 業(yè)務(wù)實(shí)體這個(gè)概念中應(yīng)該只有業(yè)務(wù)邏輯,沒有別的。
  • 業(yè)務(wù)實(shí)體這個(gè)概念只要求我們將關(guān)鍵業(yè)務(wù)數(shù)據(jù)和關(guān)鍵業(yè)務(wù)邏輯綁定在一個(gè)獨(dú)立的軟件模塊內(nèi)。
  • 業(yè)務(wù)實(shí)體不一定是類

4.用例(usecase)

用例和業(yè)務(wù)實(shí)體應(yīng)該是應(yīng)用當(dāng)中最重要的,所以我們的單元測(cè)試最低的要求就是要覆蓋所有的 usecase 邏輯,這一部分應(yīng)該保持純凈不依賴數(shù)據(jù)庫(kù),Web 等 I/O 方式

  • 用例本質(zhì)上就是關(guān)于如何操作一個(gè)自動(dòng)化系統(tǒng)的描述,它定義了用戶需要提供的輸入數(shù)據(jù)、用戶應(yīng)該得到的輸出信息以及產(chǎn)生輸出所應(yīng)該采取的處理步驟。
  • 用例中包含了對(duì)如何調(diào)用業(yè)務(wù)實(shí)體中的關(guān)鍵業(yè)務(wù)邏輯的定義。簡(jiǎn)而言之,用例控制著業(yè)務(wù)實(shí)體之間的交互方式。
  • 用例除非正式地描述了數(shù)據(jù)流入/流出接口以外,并不詳細(xì)描述用戶界面。
  • 用例并不描述系統(tǒng)與用戶之間的接口,它只描述該應(yīng)用在某些特定情景下的業(yè)務(wù)邏輯,這些業(yè)務(wù)邏輯所規(guī)范的是用戶與業(yè)務(wù)實(shí)體之間的交互方式,它與數(shù)據(jù)流入/流出系統(tǒng)的方式無關(guān)。
  • 業(yè)務(wù)實(shí)體并不會(huì)知道是哪個(gè)業(yè)務(wù)用例在控制它們,這也是依賴反轉(zhuǎn)原則(DIP)的另一個(gè)應(yīng)用情景
  • 為什么業(yè)務(wù)實(shí)體屬于高層概念,而用例屬于低層概念呢?因?yàn)橛美枋龅氖且粋€(gè)特定的應(yīng)用情景,這樣一來,用例必然會(huì)更靠近系統(tǒng)的輸入和輸出。

5.選擇直接在數(shù)據(jù)結(jié)構(gòu)中使用對(duì)業(yè)務(wù)實(shí)體對(duì)象的引用。畢竟,業(yè)務(wù)實(shí)體與請(qǐng)求/響應(yīng)模型之間有很多相同的數(shù)據(jù)。但請(qǐng)一定不要這樣做!這兩個(gè)對(duì)象存在的意義是非常、非常不一樣的。隨著時(shí)間的推移,這兩個(gè)對(duì)象會(huì)以不同的原因、不同的速率發(fā)生變更。

6.這些業(yè)務(wù)邏輯應(yīng)該保持純凈,不要摻雜用戶界面或者所使用的數(shù)據(jù)庫(kù)相關(guān)的東西。在理想情況下,這部分代表業(yè)務(wù)邏輯的代碼應(yīng)該是整個(gè)系統(tǒng)的核心,其他低層概念的實(shí)現(xiàn)應(yīng)該以插件形式接入系統(tǒng)中。業(yè)務(wù)邏輯應(yīng)該是系統(tǒng)中最獨(dú)立、復(fù)用性最高的代碼。

再次強(qiáng)調(diào)了不要偷懶,今天剛好看到之前寫的一個(gè)反面例子的代碼,代碼里面有一個(gè) GetA 函數(shù),從數(shù)據(jù)庫(kù)當(dāng)中獲取A對(duì)象數(shù)據(jù)和一些統(tǒng)計(jì)數(shù)據(jù),這個(gè)函數(shù)中的統(tǒng)計(jì)數(shù)據(jù)部分其實(shí)只有在一個(gè) Web 頁(yè)面的接口中使用到,但是為了偷懶,在其他地方查詢的時(shí)候也調(diào)用了這個(gè)函數(shù),導(dǎo)致最后很多地方的接口性能都由于這個(gè)沒用的統(tǒng)計(jì)數(shù)據(jù)多耗費(fèi)了將近 1s 的時(shí)間。

第 21 章 尖叫的軟件架構(gòu)

1.架構(gòu)設(shè)計(jì)的主題

  • 軟件的系統(tǒng)架構(gòu)應(yīng)該為該系統(tǒng)的用例提供支持。這就像住宅和圖書館的建筑計(jì)劃滿篇都在非常明顯地凸顯這些建筑的用例一樣,軟件系統(tǒng)的架構(gòu)設(shè)計(jì)圖也應(yīng)該非常明確地凸顯該應(yīng)用程序會(huì)有哪些用例

2.架構(gòu)設(shè)計(jì)的核心目標(biāo)

  • 一個(gè)良好的架構(gòu)設(shè)計(jì)應(yīng)該圍繞著用例來展開,這樣的架構(gòu)設(shè)計(jì)可以在脫離框架、工具以及使用環(huán)境的情況下完整地描述用例
  • 良好的架構(gòu)設(shè)計(jì)應(yīng)該盡可能地允許用戶推遲和延后決定采用什么框架、數(shù)據(jù)庫(kù)、Web 服務(wù)以及其他與環(huán)境相關(guān)的工具
  • 良好的架構(gòu)設(shè)計(jì)應(yīng)該只關(guān)注用例,并能將它們與其他的周邊因素隔離。

3.可測(cè)試的架構(gòu)設(shè)計(jì)

  • 我們?cè)谶\(yùn)行測(cè)試的時(shí)候不應(yīng)該運(yùn)行 Web 服務(wù),也不應(yīng)該需要連接數(shù)據(jù)庫(kù)。我們測(cè)試的應(yīng)該只是一個(gè)簡(jiǎn)單的業(yè)務(wù)實(shí)體對(duì)象,沒有任何與框架、數(shù)據(jù)庫(kù)相關(guān)的依賴關(guān)系。

4.一個(gè)系統(tǒng)的架構(gòu)應(yīng)該著重于展示系統(tǒng)本身的設(shè)計(jì),而并非該系統(tǒng)所使用的框架

用例是架構(gòu)設(shè)計(jì)當(dāng)中最應(yīng)該關(guān)注的部分,框架數(shù)據(jù)庫(kù)Web服務(wù)的選擇都是細(xì)節(jié),這些細(xì)節(jié)應(yīng)該延后選擇,我們的用例不應(yīng)該依賴這些細(xì)節(jié),這樣才能很好的測(cè)試

第 22 章 整潔架構(gòu)

1.按照不同關(guān)注點(diǎn)對(duì)軟件進(jìn)行切割。也就是說,這些架構(gòu)都會(huì)將軟件切割成不同的層,至少有一層是只包含該軟件的業(yè)務(wù)邏輯的,而用戶接口、系統(tǒng)接口則屬于其他層。

2.特點(diǎn)

  • 獨(dú)立于框架:這些系統(tǒng)的架構(gòu)并不依賴某個(gè)功能豐富的框架之中的某個(gè)函數(shù)。
  • 可被測(cè)試:這些系統(tǒng)的業(yè)務(wù)邏輯可以脫離 UI、數(shù)據(jù)庫(kù)、Web 服務(wù)以及其他的外部元素來進(jìn)行測(cè)試。
  • 獨(dú)立于 UI:這些系統(tǒng)的 UI 變更起來很容易,不需要修改其他的系統(tǒng)部分。
  • 獨(dú)立于數(shù)據(jù)庫(kù):我們可以輕易將這些系統(tǒng)使用的
  • 獨(dú)立于任何外部機(jī)構(gòu):這些系統(tǒng)的業(yè)務(wù)邏輯并不需要知道任何其他外部接口的存在。

image.png

1.依賴關(guān)系規(guī)則

  • 外層圓代表的是機(jī)制,內(nèi)層圓代表的是策略。
  • 源碼中的依賴關(guān)系必須只指向同心圓的內(nèi)層,即由低層機(jī)制指向高層策略。
  • 外層圓中使用的數(shù)據(jù)格式也不應(yīng)該被內(nèi)層圓中的代碼所使用,尤其是當(dāng)數(shù)據(jù)格式是由外層圓的框架所生成時(shí)。

2.業(yè)務(wù)實(shí)體

  • 業(yè)務(wù)實(shí)體這一層中封裝的是整個(gè)系統(tǒng)的關(guān)鍵業(yè)務(wù)邏輯,一個(gè)業(yè)務(wù)實(shí)體既可以是一個(gè)帶有方法的對(duì)象,也可以是一組數(shù)據(jù)結(jié)構(gòu)和函數(shù)的集合。

3.用例

  • 軟件的用例層中通常包含的是特定應(yīng)用場(chǎng)景下的業(yè)務(wù)邏輯,這里面封裝并實(shí)現(xiàn)了整個(gè)系統(tǒng)的所有用例。這些用例引導(dǎo)了數(shù)據(jù)在業(yè)務(wù)實(shí)體之間的流入/流出,并指揮著業(yè)務(wù)實(shí)體利用其中的關(guān)鍵業(yè)務(wù)邏輯來實(shí)現(xiàn)用例的設(shè)計(jì)目標(biāo)。

4.接口適配器

  • 軟件的接口適配器層中通常是一組數(shù)據(jù)轉(zhuǎn)換器,它們負(fù)責(zé)將數(shù)據(jù)從對(duì)用例和業(yè)務(wù)實(shí)體而言最方便操作的格式,轉(zhuǎn)化成外部系統(tǒng)(譬如數(shù)據(jù)庫(kù)以及 Web)最方便操作的格式。

5.層次越往內(nèi),其抽象和策略的層次越高,同時(shí)軟件的抽象程度就越高,其包含的高層策略就越多。最內(nèi)層的圓中包含的是最通用、最高層的策略,最外層的圓包含的是最具體的實(shí)現(xiàn)細(xì)節(jié)。

6.這里最重要的是這個(gè)跨邊界傳輸?shù)膶?duì)象應(yīng)該有一個(gè)獨(dú)立、簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu)。

7.「不要投機(jī)取巧地直接傳遞業(yè)務(wù)實(shí)體或數(shù)據(jù)庫(kù)記錄對(duì)象?!?/p>

看過前面的部分再來看整潔架構(gòu)這一章節(jié)會(huì)發(fā)現(xiàn)非常的自然

第 23 章 展示器和謙卑對(duì)象

1.謙卑對(duì)象模式

  • 謙卑對(duì)象模式最初的設(shè)計(jì)目的是幫助單元測(cè)試的編寫者區(qū)分容易測(cè)試的行為與難以測(cè)試的行為,并將它們隔離。

2.展示器與視圖

  • 視圖部分屬于難以測(cè)試的謙卑對(duì)象。這種對(duì)象的代碼通常應(yīng)該越簡(jiǎn)單越好,它只應(yīng)負(fù)責(zé)將數(shù)據(jù)填充到 GUI 上,而不應(yīng)該對(duì)數(shù)據(jù)進(jìn)行任何處理。
  • 展示器則是可測(cè)試的對(duì)象。展示器的工作是負(fù)責(zé)從應(yīng)用程序中接收數(shù)據(jù),然后按視圖的需要將這些數(shù)據(jù)格式化,以便視圖將其呈現(xiàn)在屏幕上。
  • 展示器則是可測(cè)試的對(duì)象。展示器的工作是負(fù)責(zé)從應(yīng)用程序中接收數(shù)據(jù),然后按視圖的需要將這些數(shù)據(jù)格式化,以便視圖將其呈現(xiàn)在屏幕上。
  • 視圖部分除了加載視圖模型所需要的值,不應(yīng)該再做任何其他事情。因此,我們才能說視圖是謙卑對(duì)象

3.數(shù)據(jù)庫(kù)網(wǎng)關(guān)

  • 這些實(shí)現(xiàn)也應(yīng)該都屬于謙卑對(duì)象,它們應(yīng)該只利用 SQL 或其他數(shù)據(jù)庫(kù)提供的接口來訪問所需要的數(shù)據(jù)。
  • 交互器盡管不屬于謙卑對(duì)象,卻是可測(cè)試的,

4.數(shù)據(jù)映射器(ORM)

這樣的 ORM 系統(tǒng)應(yīng)該屬于系統(tǒng)架構(gòu)中的哪一層呢?當(dāng)然是數(shù)據(jù)庫(kù)層。ORM 其實(shí)就是在數(shù)據(jù)庫(kù)和數(shù)據(jù)庫(kù)網(wǎng)關(guān)接口之間構(gòu)建了另一種謙卑對(duì)象的邊界。

5.因?yàn)榭邕吔绲耐ㄐ趴隙ㄐ枰玫侥撤N簡(jiǎn)單的數(shù)據(jù)結(jié)構(gòu),而邊界會(huì)自然而然地將系統(tǒng)分割成難以測(cè)試的部分與容易測(cè)試的部分,所以通過在系統(tǒng)的邊界處運(yùn)用謙卑對(duì)象模式,我們可以大幅地提高整個(gè)系統(tǒng)的可測(cè)試性。

這里主要是將很難進(jìn)行單元測(cè)試的行為和容易測(cè)試的行為進(jìn)行分離,很難被測(cè)試的行為常常會(huì)被分離成為一個(gè)謙卑對(duì)象,這個(gè)對(duì)象非常的簡(jiǎn)單,不會(huì)包含很多邏輯

第 24 章 不完全邊界

1.構(gòu)建不完全邊界的方式

架構(gòu)是需要取舍的,我們不可能每一項(xiàng)都做的很完美,邊界的劃分也是這樣,所以就有了不完全的邊界

  • 構(gòu)建不完全邊界的一種方式就是在將系統(tǒng)分割成一系列可以獨(dú)立編譯、獨(dú)立部署的組件之后,再把它們構(gòu)建成一個(gè)組件。
  • 單向邊界
  • 門戶模式

第 25 章 層次與邊界

1.「過度的工程設(shè)計(jì)往往比工程設(shè)計(jì)不足還要糟糕」

2.現(xiàn)實(shí)就是這樣。作為軟件架構(gòu)師,我們必須有一點(diǎn)未卜先知的能力。有時(shí)候要依靠猜測(cè)——當(dāng)然還要用點(diǎn)腦子。軟件架構(gòu)師必須仔細(xì)權(quán)衡成本,決定哪里需要設(shè)計(jì)架構(gòu)邊界,以及這些地方需要的是完整的邊界,還是不完全的邊界,還是可以忽略的邊界

3.架構(gòu)師必須持續(xù)觀察系統(tǒng)的演進(jìn),時(shí)刻注意哪里可能需要設(shè)計(jì)邊界,然后仔細(xì)觀察這些地方會(huì)由于不存在邊界而出現(xiàn)哪些問題。

不要過度優(yōu)化,但是也不要什么都不管的一把梭,架構(gòu)師需要演進(jìn)和取舍的,沒有完美的架構(gòu)只有不斷持續(xù)演進(jìn)優(yōu)化的架構(gòu)。

第 26 章 Main 組件

  • Main 是最細(xì)節(jié)化的部分
  • Main 組件的任務(wù)是創(chuàng)建所有的工廠類、策略類以及其他的全局設(shè)施,并最終將系統(tǒng)的控制權(quán)轉(zhuǎn)交給最高抽象層的代碼來處理。
  • Main 組件中的依賴關(guān)系通常應(yīng)該由依賴注入框架來注入。
  • 我們?cè)谶@里的重點(diǎn)是要說明 Main 組件是整個(gè)系統(tǒng)中的一個(gè)底層模塊,它處于整潔架構(gòu)的最外圈,主要負(fù)責(zé)為系統(tǒng)加載所有必要的信息,然后再將控制權(quán)轉(zhuǎn)交回系統(tǒng)的高層組件。

main 是一個(gè)程序的入口,這是最細(xì)節(jié)的部分,因?yàn)橹盀榱撕芏鄸|西不被依賴,我們一般會(huì)采用接口來實(shí)現(xiàn)依賴反轉(zhuǎn),這時(shí)候就會(huì)導(dǎo)致我們所有的依賴關(guān)系的構(gòu)建都需要在 main 中進(jìn)行完成,所以一般而言我們會(huì)在 main 中引入依賴注入框架。

第 27 章 服務(wù):宏觀與微觀

1.所謂的服務(wù)本身只是一種比函數(shù)調(diào)用方式成本稍高的,分割應(yīng)用程序行為的一種形式,與系統(tǒng)架構(gòu)無關(guān)。

2.服務(wù)所帶來的好處?

  • 解耦合的謬論
  • 獨(dú)立開發(fā)部署的謬論
  • 這種理念有一些道理——但也僅僅是一些而已。首先,**無數(shù)歷史事實(shí)證明,大型系統(tǒng)一樣可以采用單體模式,或者組件模式來構(gòu)建,不一定非得服務(wù)化。**因此服務(wù)化并不是構(gòu)建大型系統(tǒng)的唯一選擇。

3.橫跨型變更(cross-cutting concern)問題,它是所有的軟件系統(tǒng)都要面對(duì)的問題,無論服務(wù)化還是非服務(wù)化的。

4.服務(wù)也可以按照 SOLID 原則來設(shè)計(jì),按照組件結(jié)構(gòu)來部署,這樣就可以做到在添加/刪除組件時(shí)不影響服務(wù)中的其他組件。

5.系統(tǒng)的架構(gòu)邊界事實(shí)上并不落在服務(wù)之間,而是穿透所有服務(wù),在服務(wù)內(nèi)部以組件的形式存在

6.「服務(wù)邊界并不能代表系統(tǒng)的架構(gòu)邊界,服務(wù)內(nèi)部的組件邊界才是?!?/p>

7.系統(tǒng)的架構(gòu)是由系統(tǒng)內(nèi)部的架構(gòu)邊界,以及邊界之間的依賴關(guān)系所定義的,與系統(tǒng)中各組件之間的調(diào)用和通信方式無關(guān)。

雖然現(xiàn)在微服務(wù)架構(gòu)非常火熱,基本上所有的服務(wù)都是拆分了服務(wù),但是拆分了服務(wù)并不一定表示就解耦合了,也并不一定就真的能獨(dú)立部署,想一想這是現(xiàn)在很常見的,一個(gè)應(yīng)用必須要和另外一個(gè)應(yīng)用一同上線,根本做不了獨(dú)立部署。

第 28 章 測(cè)試邊界

  • 可測(cè)試性設(shè)計(jì)
  • 如果測(cè)試代碼與系統(tǒng)是強(qiáng)耦合的,它就得隨著系統(tǒng)變更而變更。哪怕只是系統(tǒng)中組件的一點(diǎn)小變化,都可能會(huì)導(dǎo)致許多與之相耦合的測(cè)試出現(xiàn)問題,需要做出相應(yīng)的變更。
  • 軟件設(shè)計(jì)的第一條原則——不管是為了可測(cè)試性還是其他什么東西——是不變的,就是不要依賴于多變的東西。
  • 沒有按系統(tǒng)組成部分來設(shè)計(jì)的測(cè)試代碼,往往是非常脆弱且難以維護(hù)的。

不變的組件不要依賴多變的東西,這樣會(huì)導(dǎo)致非常難以測(cè)試

第 29 章 整潔的嵌入式架構(gòu)

1.雖然軟件本身并不會(huì)隨時(shí)間推移而磨損,但硬件及其固件卻會(huì)隨時(shí)間推移而過時(shí),隨即也需要對(duì)軟件做相應(yīng)改動(dòng)

2.雖然軟件質(zhì)量本身并不會(huì)隨時(shí)間推移而損耗,但是未妥善管理的硬件依賴和固件依賴卻是軟件的頭號(hào)殺手。

3.但如果你在代碼中嵌入了 SQL 或者是代碼中引入了對(duì)某個(gè)平臺(tái)的依賴的話,其實(shí)就是在寫固件代碼。

4.軟件構(gòu)建過程中的三個(gè)階段

  • “先讓代碼工作起來”——如果代碼不能工作,就不能產(chǎn)生價(jià)值。
  • “然后再試圖將它變好”——通過對(duì)代碼進(jìn)行重構(gòu),讓我們自己和其他人更好地理解代碼,并能按照需求不斷地修改代碼
  • “最后再試著讓它運(yùn)行得更快”——按照性能提升的“需求”來重構(gòu)代碼。

5.整潔的嵌入式架構(gòu)就是可測(cè)試的嵌入式架構(gòu)

6.軟件與固件集成在一起也屬于設(shè)計(jì)上的反模式(anti-pattern)

軟件并不會(huì)隨著時(shí)間磨損但是硬件是會(huì)過時(shí)的,而且換的還非常頻繁,這時(shí)候我們就必須要把硬件以及固件代碼給隔離起來,對(duì)了不要認(rèn)為我們不做嵌入式開發(fā)平時(shí)就很少接觸到這個(gè),SQL 語句其實(shí)也是一種固件代碼

第六部門 實(shí)現(xiàn)細(xì)節(jié)

第 30 章 數(shù)據(jù)庫(kù)只是實(shí)現(xiàn)細(xì)節(jié)

  • 就數(shù)據(jù)庫(kù)與整個(gè)系統(tǒng)架構(gòu)的關(guān)系打個(gè)比方,它們之間就好比是門把手和整個(gè)房屋架構(gòu)的關(guān)系
  • 但當(dāng)問題涉及數(shù)據(jù)存儲(chǔ)時(shí),這方面的操作通常是被封裝起來,隔離在業(yè)務(wù)邏輯之外的
  • 數(shù)據(jù)本身很重要,但數(shù)據(jù)庫(kù)系統(tǒng)僅僅是一個(gè)實(shí)現(xiàn)細(xì)節(jié)。

數(shù)據(jù)很重要,但是數(shù)據(jù)庫(kù)系統(tǒng)是一個(gè)細(xì)節(jié),書上這一章用了一個(gè)例子說明有時(shí)候可能真的用不到數(shù)據(jù)庫(kù)。換個(gè)常見的例子,我們可能系統(tǒng)剛開始的時(shí)候使用 SQlite 就可以,隨著業(yè)務(wù)發(fā)展用上了 MySQL,然后隨著并發(fā)的提高又會(huì)引入緩存組件,這些變化其實(shí)和業(yè)務(wù)邏輯都沒有關(guān)系,數(shù)據(jù)庫(kù)的變化是不應(yīng)該影響到業(yè)務(wù)邏輯的

第 31 章 Web 是實(shí)現(xiàn)細(xì)節(jié)

  • GUI 只是一個(gè)實(shí)現(xiàn)細(xì)節(jié)。而 Web 則是 GUI 的一種,所以也是一個(gè)實(shí)現(xiàn)細(xì)節(jié)。作為一名軟件架構(gòu)師,我們需要將這類細(xì)節(jié)與核心業(yè)務(wù)邏輯隔離開來。

第 32 章 應(yīng)用程序框架是實(shí)現(xiàn)細(xì)節(jié)

  • 我們可以使用框架——但要時(shí)刻警惕,別被它拖住
  • 畢竟 Main 組件作為系統(tǒng)架構(gòu)中最低層、依賴最多的組件,它依賴于 Spring 并不是問題。

框架的選擇要慎重,我們業(yè)務(wù)邏輯本身不能依賴框架

第 33 章 案例分析:視頻銷售網(wǎng)站

  • 系統(tǒng)架構(gòu)設(shè)計(jì)中的第一步,是識(shí)別系統(tǒng)中的各種角色和用例

這一步看起來簡(jiǎn)單,但是非??简?yàn)一個(gè)人的功力

第 34 章 拾遺

  • 分層架構(gòu)無法展現(xiàn)具體的業(yè)務(wù)領(lǐng)域信息。把兩個(gè)不同業(yè)務(wù)領(lǐng)域的、但是都采用了分層架構(gòu)的代碼進(jìn)行對(duì)比,你會(huì)發(fā)現(xiàn)它們的相似程度極高
  • 寬松的分層架構(gòu),允許某些層跳過直接相鄰的鄰居。
  • 一個(gè)架構(gòu)設(shè)計(jì)原則——內(nèi)容是“Web 控制器永遠(yuǎn)不應(yīng)該直接訪問數(shù)據(jù)層”。
  • 系統(tǒng)由一個(gè)或者多個(gè)容器組成(例如 Web 應(yīng)用、移動(dòng) App、獨(dú)立應(yīng)用、數(shù)據(jù)庫(kù)、文件系統(tǒng)),每個(gè)容器包含一個(gè)或多個(gè)組件,每個(gè)組件由一個(gè)或多個(gè)類組成。
  • 如果不考慮具體實(shí)現(xiàn)細(xì)節(jié),再好的設(shè)計(jì)也無法長(zhǎng)久。必須要將設(shè)計(jì)映射到對(duì)應(yīng)的代碼結(jié)構(gòu)上,考慮如何組織代碼樹,以及在編譯期和運(yùn)行期采用哪種解耦合的模式。
  • 最好能利用編譯器來維護(hù)所選的系統(tǒng)架構(gòu)設(shè)計(jì)風(fēng)格,小心防范來自其他地方的耦合模式,例如數(shù)據(jù)結(jié)構(gòu)

image.png

這一章對(duì)比了四種架構(gòu)風(fēng)格,同時(shí)提出了,架構(gòu)設(shè)計(jì)是需要考慮實(shí)現(xiàn)細(xì)節(jié)的,設(shè)計(jì)需要映射到代碼結(jié)構(gòu)和代碼樹上,這個(gè)其實(shí)和最開始的“軟件架構(gòu)師自身需要是程序員,并且必須一直堅(jiān)持做一線程序員”交相呼應(yīng)。如果可以在編譯時(shí)解決的問題,就不要放到運(yùn)行時(shí),編譯的問題往往要比運(yùn)行時(shí)的問題好解決,這也是為什么 Go 的依賴注入框架我更加推薦 wire 的原因,同理作者提出了 如果要防止直接中 web控制器調(diào)用數(shù)據(jù)層,那么我們就不應(yīng)該將數(shù)據(jù)層(repo)暴露出來,只需要暴露 usecase 就好了。

總結(jié)

之前其實(shí)也大概了解過整潔架構(gòu),從最開始覺得它又臭又長(zhǎng),到現(xiàn)在工作兩三年后覺得“不聽老人言,吃虧在眼前”,當(dāng)我們?cè)趯?duì)一個(gè)架構(gòu)或者是事務(wù)進(jìn)行批判的時(shí)候一定要了解它面對(duì)的場(chǎng)景以及它的理念,這是最重要的。當(dāng)然軟件領(lǐng)域是沒有銀彈的,我們需要做的是吸收每一種思想,在不同的場(chǎng)景下做不同的取舍,接下來會(huì)有幾篇文章結(jié)合毛老師課上講的 Go 工程化相關(guān)的內(nèi)容,以及我在工作當(dāng)中進(jìn)行的一些總結(jié)最后提出一種當(dāng)下我覺得的 Go 項(xiàng)目的組織方式,這種方式不是最好的,但是我覺得是現(xiàn)階段最適合的。推薦大家在仔細(xì)的閱讀一下本書,期望你能有更多的收獲。

參考文獻(xiàn)架構(gòu)整潔之道-羅伯特·C·馬丁-微信讀書

文章來源博客地址: https://lailin.xyz 

 

責(zé)任編輯:姜華 來源: mohuishou
相關(guān)推薦

2021-12-24 09:00:43

Go語言進(jìn)程

2012-08-01 09:38:17

代碼整潔

2022-04-18 09:41:14

Go架構(gòu)設(shè)計(jì)

2019-05-14 09:31:16

架構(gòu)整潔軟件編程范式

2012-08-01 09:23:31

代碼

2021-01-06 14:42:09

前端Typescript代碼

2023-09-15 10:33:45

前端工程化commit

2021-03-07 09:19:31

React代碼整潔代碼的實(shí)踐

2021-12-27 08:27:18

RepoGo 代碼

2022-12-01 07:46:01

工程化工具

2025-06-27 06:38:19

2021-05-18 19:18:50

前端工程化工程

2023-12-25 09:49:01

Golang架構(gòu)Go-Kit

2020-12-09 10:49:33

代碼開發(fā)GitHub

2020-02-29 16:00:20

代碼開發(fā)程序員

2021-11-22 06:17:26

npm工程化工具

2023-09-28 08:34:26

Docker微服務(wù)

2022-07-26 17:19:11

前端前端工程化

2022-10-09 14:50:24

前端pnpm工具

2025-03-26 03:20:00

點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日本 欧美 三级 高清 视频 | 综合久久网 | 亚洲一区精品在线 | 91就要激情 | 一区二区三区四区免费视频 | 91精品国产一区二区三区蜜臀 | 99久久免费精品国产免费高清 | 国产视频1区| 成人av电影免费在线观看 | 91精品国产自产精品男人的天堂 | 久久久久久久av麻豆果冻 | 国产视频1区 | 欧美精品一区二区三区蜜桃视频 | 国产精品久久久久久久久久久久久 | 国产91丝袜在线播放 | 日韩精品无码一区二区三区 | 欧美一区二区三区日韩 | 国偷自产av一区二区三区 | 色综合视频| 久久久久国产精品一区二区 | 国产精品美女久久久 | 美国一级黄色片 | 免费1区2区3区 | 天天插日日操 | www.伊人.com| 一区二区三区国产精品 | 不卡欧美| 亚洲日本一区二区三区四区 | 在线一区 | 欧美精品电影一区 | 伊人亚洲 | 日韩免费中文字幕 | 九九热精品视频 | 亚洲香蕉在线视频 | 日韩欧美亚洲 | 久久久久久久久中文字幕 | 国产美女一区二区三区 | 色视频在线观看 | av特级毛片 | 日本在线一区二区 | 亚洲日本三级 |