代碼精進之路-讀後感

總結

  1. 函數名要具體,名字要體現出是做什麼的,而不是怎麼做.
  2. 一個方法不要超過20行.
  3. 函數職責單一,一個方法只做一件事情.
  4. 組合函數模式:所有的公有函數(入口函數)讀起來像一系列執行步 驟的概要,而這些步驟的真正實現細節是在私有函數裏面.函數體中的內容必須在同一個抽象層次 上
  5. 分治:分治的價值在於,我們不應該試着在同一時間把整個問題域都塞進 自己的大腦,而應該試着以某種方式去組織問題,以便在一個時刻專注 於一個特定的部分。這樣做的目的是儘量降低在任意時間所要思考問題 的複雜度。
  6. 寫代碼的兩次創造:1.實現功能. 2.重構優化
  7. 結構化思維(抽象)
    1. 邏輯
      1. 時間順序:第一,第二,第三
      2. 空間順序:前端,後端,數據庫
      3. 演繹順序:大前提,小前提,結論
      4. 程度順序:最重要,次重要,不重要
  8. 套路:從“Why、Who、When、Where、 What、How和How much”全面思考問題.

第一章 命名

函數名

函數命名要具體,空泛的命名沒有意義。例如,processData()就不是一個好的命名,因爲所有的方法都是對數據的處理,這樣的命名並沒 有表明要做的事情,相比之下,validateUserCredentials()或者eliminateDuplicateRequests()就要好許多。

函數的命名要體現做什麼,而不是怎麼做。假如我們將僱員信息存 儲在一個棧中,現在要從棧中獲取最近存儲的一個僱員信息,那麼 getLatestEmployee()就比popRecord()要好,因爲棧數據結構是底層實現 細節,命名應該提升抽象層次、體現業務語義。合理的命名可以使你省 掉記住“出棧”的腦力步驟,你只需要簡單地說“取最近僱員的信息”。

輔助類

對於輔助類,儘量不要用Helper、Util之類的後綴,因爲其含義太 過籠統,容易破壞SRP(單一職責原則)

比如對於處理CSV,可以這 樣寫:

CSVHelper.parse(String)
CSVHelper.create(int[])

但是我更建議將CSVHelper拆開:

CSVParser.parse(String)
CSVBuilder.create(int[])

方法名約定

CRUD操作 方法名約定
新增 create
添加 add
刪除 remove
修改 update
查詢(單個結果) get
查詢(多個結果) list
分頁查詢 page
統計 count

使用對仗詞

遵守對仗詞的命名規則有助於保持一致性,從而提高代碼的可讀 性。像first/last這樣的對仗詞就很容易理解;而像fileOpen()和fClose()這 樣的組合則不對稱,容易使人迷惑。下面列出一些常見的對仗詞組:

add/remove 
increment/decrement 
open/close 
begin/end 
insert/delete 
show/hide 
create/destroy 
lock/unlock 
source/target 
first/last
min/max 
start/stop 
get/set 
next/previous 
up/down 
old/new

後置限定詞

很多程序中會有表示計算結果的變量,例如總額、平均值、最大值 等。如果你要用類似Total、Sum、Average、Max、Min這樣的限定詞來 修改某個命名,那麼記住把限定詞加到名字的最後,並在項目中貫徹執 行,保持命名風格的一致性。

這種方法有很多優點。首先,變量名中最重要的部分,即爲這一變 量賦予主要含義的部分應位於最前面,這樣可以突出顯示,並會被首先 閱讀到。其次,可以避免同時在程序中使用totalRevenue和revenueTotal 而產生的歧義。如果貫徹限定詞後置的原則,我們就能收穫一組非常優 雅、具有對稱性的變量命名,例如revenueTotal(總收入)、 expenseTotal(總支出)、revenueAverage(平均收入)和 expenseAverage(平均支出)。

需要注意的一點是Num這個限定詞,Num放在變量名的結束位置表 示一個下標,customerNum表示的是當前客戶的序號。爲了避免Num帶 來的麻煩,我建議用Count或者Total來表示總數,用Id表示序號。這 樣,customerCount表示客戶的總數,customerId表示客戶的編號。

註釋

  1. 不要複述功能

爲了複述代碼功能而存在的註釋,主要作用是彌補我們表達意圖時 遭遇的失敗,這時要考慮這樣的註釋是否是必需的。如果編程語言足夠 有表達力,或者我們擅長用代碼顯性化地表達意圖,那麼也許根本就不 需要註釋。因此,在寫註釋時,你應該自省自己是否在表達能力上存在 不足,真正的高手是儘量不寫註釋。

  1. 要解釋背後的意圖

註釋要能夠解釋代碼背後的意圖,而不是對功能的簡單重複.

第二章 規範

日誌規範

1.ERROR級別

ERROR表示不能自己恢復的錯誤,需要立即被關注和解決。例 如,數據庫操作錯誤、I/O錯誤(網絡調用超時、文件讀取錯誤等)、 未知的系統錯誤(NullPointerException、OutOfMemoryError等)。

對於ERROR,我們不僅要打印線程堆棧,最好打印出一定的上下 文(鏈路TraceId、用戶Id、訂單Id、外部傳來的關鍵數據),以便於排 查問題

ERROR要接入監控和報警系統。ERROR需要人工介入處理,及時 止損,否則會影響系統的可用性。當然也不能濫用ERROR,否則就會 出現“狼來了”的情況。我在實際工作中曾碰到過系統每天會發出上千條 錯誤報警的情況,導致根本沒有人看報警內容,在真正出現問題時,也 沒有人關注,從而引發線上故障。因此,一定要做好ERROR輸出的場 景定義和規範,再配合監控治理,雙管齊下,確保線上系統的穩定。

2.WARN級別

對於可預知的業務問題,最好不要用ERROR輸出日誌,以免污染 報警系統。例如,參數校驗不通過、沒有訪問權限等業務異常,就不應 該用ERROR輸出。

需要注意的是,在短時間內產生過多的WARN日誌,也是一種系統 不健康的表現。因此,我們有必要爲WARN配置一個適當閾值的報警, 比如訪問受限WARN超過100次/分,則發出報警。這樣在WARN日誌過 於頻繁時,我們能及時收到系統報警,去跟進用戶問題。例如,如果是 產品設計上有缺陷導致用戶頻繁出現操作卡點,可以考慮做一下流程或 者產品上的優化。

3.INFO級別

INFO用於記錄系統的基本運行過程和運行狀態。

通常來說,優先根據INFO日誌可初步定位,主要包括系統狀態變化日誌、業務流程的核心處理、關鍵動作和業務流程的狀態變化。適當 的INFO可以協助我們排查問題,但是切忌把INFO當成DEBUG使用,這 樣會導致記錄的數據過多,一方面影響系統性能,日誌文件增長過快, 消耗不必要的存儲資源;另一方面也不利於閱讀日誌文件。

4.DEBUG級別

DEBUG是輸出調試信息,如request/response的對象內容。在輸出對 象內容時,要覆蓋Object的toString方法,否則輸出的是對象的內存地 址,就起不到調試的作用了。

通常在開發和預發環境下,DEBUG日誌會打開,以方便開發和調 試。而在線上環境,DEBUG開關需要關閉,因爲在生產環境下開啓 DEBUG會導致日誌量非常大,其損耗是難以接受的。只有當線上出現 bug或者棘手的問題時,纔可以動態地開啓DEBUG。爲了防止日誌量過 大,我們可以採用分佈式配置工具來實現基於requestId判斷的日誌過 濾,從而只打印我們所需請求的DEBUG日誌。

異常

我建議在業務系統中設定兩個異常,分別是 BizException(業務異常)和SysException(系統異常),而且這兩個異常都應該是Unchecked Exception。

千萬不要在業務處理內部到處使用try/catch打印錯誤日誌,這樣會 使功能代碼和業務代碼纏繞在一起,讓代碼顯得很凌亂,並且影響代碼 的可讀性。

第三章 函數

參數數量

最理想的參數數量是零(零參數函數),其次是一(一元函數), 再次是二(二元函數),應儘量避免三(三元函數)。

如果函數需要3 個以上參數,就說明其中一些參數應該封裝爲類了。

短小的函數

如果是Java語言,我建議一個方法不要超過20 行代碼,當我把這個規定作爲團隊代碼審查的硬性指標後,發現代碼質 量得到了顯著的改善。

職責單一

即一個方法只做一件事情,也就是函數級別的單一職責原則.

不僅可以提升代碼的可讀性,還能提升代碼的可複用性。 因爲職責越單一,功能越內聚,就越有可能被複用,這和代碼的行數沒 有直接的關聯性,但是有間接的關聯性。

精簡輔助代碼

所謂的輔助代碼(Assistant Code),是程序運行中必不可少的代 碼,但又不是處理業務邏輯的核心代碼,比如判空、打印日誌、鑑權、 降級和緩存檢查等。這些代碼往往會在多個函數中重複冗餘,減少輔助 代碼可以讓代碼顯得更加乾淨整潔,易於維護。

如果輔助代碼太多,會極大地干擾代碼的可讀性,讀這種代碼會讓 人抓狂,摸不着頭腦。因此,我們應該儘量減少輔助代碼對業務代碼的 干擾。讓函數中的代碼能直觀地體現業務邏輯,而不是讓業務代碼淹沒 在輔助代碼中。

組合函數模式

組合函數要求所有的公有函數(入口函數)讀起來像一系列執行步 驟的概要,而這些步驟的真正實現細節是在私有函數裏面。組合函數有 助於代碼保持精煉並易於複用。

SLAP

抽象層次一致性(Single Level of Abstration Principle,SLAP),是 和組合函數密切相關的一個原則。組合函數要求將一個大函數拆成多個 子函數的組合,而SLAP要求函數體中的內容必須在同一個抽象層次 上。如果高層次抽象和底層細節雜糅在一起,就會顯得凌亂,難以理 解。

按照組合函數和SLAP原則,我們要在入口函數中只顯示業務處理 的主要步驟。具體的實現細節通過私有方法進行封裝,並通過抽象層次 一致性來保證,一個函數中的抽象在同一個水平上,而不是高層抽象和 實現細節混雜在一起。

在構築金字塔的過程中,要求金字塔的每一層要屬於同一個邏輯範 疇、同一個抽象層次。在這一點上,金字塔原理和SLAP是相通的,世 界就是如此奇妙,很多道理在不同的領域同樣適用。

第八章 抽象

構築金字塔

自上而下地表達,結 論先行。其中,自下而上總結概括的過程就是抽象的過程,構建金字塔 的過程就是尋找邏輯關係、抽象概括的過程。經常鍛鍊用結構化的方式去處理問題,搭建自己的金字塔,可以幫助我們理清問題的脈絡,提升 抽象能力。

金字塔結構讓我們通過抽象概括將混亂無序的信息形成不同的抽象 層次,從而便於理解和記憶,這是一個非常實用的方法論。

如何提升抽象思維

  1. 多閱讀,閱讀 的過程可以鍛鍊我們的抽象能力、想象能力,而看畫面時你的大腦會被 鋪滿,較少需要抽象和想象。
  2. 多總結,做總結最好的方式就是寫文章,記錄也是很好的總結習慣。以讀書 筆記來說,最好不要原文摘錄書中的內容,而是要用自己的話總結歸 納,這樣不僅可以加深理解,還可以提升自己的抽象思維能力。

抽象之美

“抽象”作爲名詞,代表着一種思維方式,它的偉大之處在於可以讓 我們撇開細枝末節,去把握事物更本質、更一般的特性,從而更有效地 對問題域進行分析設計。“抽象”作爲動詞,代表着一種能力,它是我們 理解概念、理清概念之間邏輯關係的基礎,也是我們面向對象分析設計 所要求的底層能力。

歸納總結,合併同類項是進行抽象活動時最有效的方法。同時,我 們也要注意到抽象是有層次性的。當一個概念無法涵蓋其外延的時候, 我們有必要提升一個抽象層次來減少它的內涵,讓其有更大的外延。

建議讀者一定要多多培養自己的抽象思維。閱讀、寫文章,以及邏 輯思維訓練都是提升抽象思維能力非常好的方式。只要堅持學習和鍛 煉,你慢慢就能體會到一種不一樣的美——抽象之美。

第九章 分治

分治的價值在於,我們不應該試着在同一時間把整個問題域都塞進 自己的大腦,而應該試着以某種方式去組織問題,以便在一個時刻專注 於一個特定的部分。這樣做的目的是儘量降低在任意時間所要思考問題 的複雜度。

分治法解題的一般步驟如下。

(1)分解:將要解決的問題劃分成若干規模較小的同類問題。

(2)求解:當子問題劃分得足夠小時,用較簡單的方法解決。

(3)合併:按原問題的要求,將子問題的解逐層合併,構成原問 題的解。

寫代碼的兩次創造

  1. 第一遍實現功能

不要試圖一次就寫出“完美的”代碼,這樣只會拖慢我們的節奏。就 像寫文章,第一遍可以寫得粗糙一點,把大概意思寫出來,然後再仔細 打磨,斟酌推敲,直到達到理想的樣子。

寫代碼也是如此,第一遍以實現功能爲主,可以允許一定的冗長和 複雜,比如有過多的縮進和嵌套循環,有過長的參數列表,名稱可以隨 意取,還會有部分的重複代碼。第一遍主要是爲了理清邏輯,爲第二遍 的重構優化做好準備。

  1. 第二遍重構優化

如果只是止步於功能實現,那麼代碼最多隻是一個半成品。而實際 情況是我們的代碼庫中有太多這樣的半成品,導致系統的複雜度不斷攀 升,越來越難維護。因此,我們需要有第二次創造——重構優化,即在 第一遍實現功能的基礎上,看一看是否可以做得更好:命名合理嗎?職 責單一嗎?滿足OCP嗎?函數是否過長?抽象是否合理?

因此,最好的優化肯定不是等系統上線後再去做,因爲這樣往往就 等於“再也不會去做”(later equals never)。優化工作本應該是我們編碼 工作的一部分,拆成兩步,主要對編碼效率上的考量。

第十章 技術人的素養

結構化思維

什麼是結構化思維呢?我給結構化思維的定義就是**“邏輯+套路”**。

所謂邏輯,是指結構之間必須是有邏輯關係的。例如,你說話時 用“第一、第二、第三”這個邏輯順序是合理的,而如果用“第一、第 二、第四”就會顯得很奇怪。實際上,組織思想的邏輯只有4種。

1.邏輯

(1)演繹順序:比如“大前提、小前提、結論”的演繹推理方式就 是演繹順序的。

(2)時間(步驟)順序:比如“第一、第二、第三”和“首先、再 者、然後”等,大多數的時間順序同時也是因果順序。

(3)空間(結構)順序:比如“前端、後端、數據”和“波士頓、紐 約、華盛頓”等,化整爲零(將整體分解爲部分)等都是空間順序。在 做空間分解時,要注意滿足“相互獨立,完全窮盡”(Mutually Exclusive Collectively Exhaustive,MECE)原則。

(4)程度(重要性)順序:比如“最重要、次重要、不重要”等。 只要我們的思想和表達在這4種邏輯順序之內,就是有邏輯的,否則就是沒有邏輯的。

2.套路

套路是指我們解決問題的方法論、路徑和經驗。比如,5W2H分析法就是非常好的,是可以幫助我們分析問題的一個“套路”。試想一下, 面對任何一個問題,你如果都能從“Why、Who、When、Where、 What、How和How much”(如圖10-6所示)這7個方面去思考,是不是 比不知道這個方法論的人用點狀模式思考要全面得多呢?

邏輯是一種能力,而套路是方法論、經驗;邏輯屬於道,而方法論 屬於術。二者都很重要,只有熟練地掌握二者,我們纔能有更好的結構 化思維。接下來,通過兩個案例來介紹結構化思維在實際工作場景中的 應用。

有目標

目標的重要性,以前是被我低估的。實際上,我之前的很多焦慮和 迷茫都是目標不清晰導致的.

先想清楚目標,然後努力實現。不管是人生大 問題,還是階段性要完成的事情,都需要目標清晰、有的放矢。

很多人表示看過很多技術文章,但是總感覺自己依然一無所 知。一個很重要的原因就是,沒有帶着目標去學習。在這個信息爆炸的 時代,如果只是碎片化地接收各個公衆號推送的文章,學習效果幾乎可 以忽略不計。在學習之前,我們一定要問自己,這次學習的目標是什 麼?是想把Redis的持久化原理搞清楚?還是把Redis的主從同步機制弄 明白?抑或是想學習整個Redis Cluster的架構體系?如果我們能夠帶着 問題與目標去搜集相關的資料並學習,就會事半功倍。這種學習模式的 效果會比碎片化閱讀好得多。

平和的心態

我的座右銘是**“動機至善,了無私心;用無爲的心,做有爲的 事”**。首先,我們做事情的出發點必須是善的。其次,“有爲的事”是指 要認真做事,認真生活;“無爲的心”代表一種平和的心態,一種活在當 下的智慧。也就是做事要積極,但是心態要放平。

精進

精進就是你每天必須進步一點點!記住,慢就是快。千萬不要忽視每天進步一點點的力量,也不要試圖一口吃成胖子, 真正的進步是滴水穿石的累積,這就是精進。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章