代碼質量的四個階段之3Rs軟件架構介紹
當提到代碼質量,我們可能會想到:代碼風格,命名,內聚,耦合,重復代碼率,圈復雜度等等。當提到代碼優化,我們可能會想到代碼風格規范,高內聚,低耦合,單一職責,開放封閉原則,約定優于配置,單元測試等等。
給你一段代碼,你能簡要扼要的說出當前代碼的質量情況,并提出優化的方向嗎?
如果覺得很難,可以參考 3Rs 軟件架構[1]。
3Rs 軟件架構對代碼質量做了分層,給優化代碼提供了方向:可讀性 => 可重用性 => 可重構。下面我們來具體了解下每層代碼的特點及優化方法。
第 4 個階段: 很難維護的代碼
這階段的代碼很難維護,俗稱shi代碼。這階段的代碼,讀起來和改起來都很難。
這階段的代碼讀起來難。例如:
- 代碼風格不一致。縮進,空格不一致。
- 謎一樣的命名。
- 很長很長的函數。
- 分支很多,嵌套很深的條件語句。
這階段的代碼改起來也難。例如:
- 大量重復代碼,導致一處有問題,要改多處。
- 高耦合的代碼導致,改一個模塊,會改對應的很多關聯模塊。
- 關聯很緊密的代碼,但離的很遠。改起來好累。
第 3 個階段:可讀的代碼(Readability)
這階段的代碼可讀性好。可讀性好換個說法就是讀起來不費腦子。它有以下的特征:
- 一致的代碼風格。空格,縮進,命名風格(駝峰,中劃線等)等在整個項目里是一致的。
- 合理的命名。“看其名而知其意”。
- 必要的注釋。代碼本身無法清晰地闡述作者的意圖時,要寫注釋。
- 沒有代碼行數很多(超過1千行)的功能:文件,組件,函數等。
- 函數的參數數量不超過4個。
- 沒有圈復雜度很高的代碼。圈復雜度高往往意味著分支多或嵌套深。
如何達到
要達到這個階段相對比較容易。
代碼檢查工具能保證代碼風格的統一。代碼檢查工具也能檢查:函數的參數個數,圈復雜度[2]等。工具有: ESLint[3],CSS Lint[4]等。代碼改動后,必須通過工具檢查通過后,才允許提交。用代碼格式化工具,可以自動修復有代碼風格問題的代碼。工具有 Prettier[5] 等。
這階段最難的就是命名了。好的命名是“看其名而知其意”,是直白的,有意義的。推薦使用故宮命名法[6]。了解更多命名的技巧見這里[7]。
第 2 個階段: 可重用的代碼(Reusability)
這階段的代碼是可重用的代碼。這個階段代碼的特點:
- 單一職責。每個模塊都只有一個職責。
- 不必要的重復代碼很少。重復代碼會導致一處有問題,要改多處。但如果過度追求沒有重復,也會導致可讀性差,不靈活的問題。
- 模塊間是低耦合,高內聚的。
如何達到
要達到這個階段需要在做代碼設計的時候,設計好模塊之間的邊界和 API,做到職責清晰,高內聚,低耦合。達到這個階段的建議:
- 多寫代碼。對之前寫的代碼做復盤。
- 多讀優秀代碼,學習借鑒好的地方。
- 學習設計原則,設計模式。
- 學習一些具體的技術:函數式編程,響應式編程,面向領域編程等等。
第 1 個階段: 可重構的代碼(Refactorability)
這階段的代碼是可重構的。這意味著,當你重構某塊代碼(不修改對外的API),不改其他代碼,其他代碼仍能正常工作。這個階段的代碼一定是低耦合的。模塊之間的連接就像樂高。
如何達到
要達到這個階段需要:
- 隔離副作用。
- 測試。
- 靜態類型。
- 下面具體來說。
隔離副作用
副作用指修改模塊外的數據。例如:修改全局變量,修改 DOM等。
在模塊代碼中,混入副作用代碼會導致如下的問題:
- 副作用讓代碼變得難以測試。當模塊依賴的外部數據發生變化后,模塊的返回值可能會變化。這讓模塊的返回變得不穩定。
- 副作用會導致模塊間的耦合。如果多個模塊都依賴某個外部數據,那這幾個外部模塊之間是耦合的。多個模塊改都可以改外部數據,數據流很混亂。
- 副作用讓我們的系統變得不可預測。如果一個模塊改了外部數據,可能會影響整個系統。
如何隔離副作用?答案是在統一的地方管理應用的全局數據。比如用 Redux[8] 或 Vuex[9]。
測試
這邊的測試指的是白盒測試。測試可以保證代碼的改動不會影響測試覆蓋部分的功能。
對前端來說,需要寫單元測試,端到端測試。每次提交代碼,所有測試都需要跑過。
靜態類型
使用靜態類型可以規避很多低級的語法和邏輯錯誤,比如參數少傳了。目前前端靜態類型主流是用 TypeScript[10]。
參考資料
[1]3Rs 軟件架構: https://github.com/ryanmcdermott/3rs-of-software-architecture
[2]圈復雜度: http://eslint.cn/docs/rules/complexity
[3]ESLint: https://eslint.org/
[4]CSS Lint: http://csslint.net/
[5]Prettier: https://prettier.io/
[6]故宮命名法: https://juejin.cn/post/6844903913892610061
[7]這里: https://www.yuque.com/fegogogo/fe/wup00n
[8]Redux: https://redux.js.org/
[9]Vuex: https://vuex.vuejs.org/zh/guide/
[10]TypeScript: https://www.typescriptlang.org/