編寫可讀代碼的藝術:末·代碼塊&測試


前言:上篇記錄了如何改變程序的“循環(huán)與邏輯”來讓代碼更有可讀性,包括幾種技巧,這些技巧都需要對代碼結(jié)構做出微小的改工,具體見這里。本文在前兩篇的基礎上,討論第三部分,在函數(shù)級別對代碼做更大的改動,以及第四部分,可讀測試代碼的重要性。


*第一部分:重新組織代碼 *

如何編寫可讀代碼

  1. 簡化命名、注釋和格式的方法,使每行代碼都言簡意賅。
  2. 梳理程序中的循環(huán)、邏輯和變量來減小復雜度并理清思路。
  3. 在函數(shù)級別解決問題,例如重新組織代碼塊,使其一次只做一件事。
  4. 編寫有效的測試代碼,使其全面簡潔,同時可讀性更高。

抽取不相關的子問題

在工程學中,主要研究的問題是關于把大問題拆成小問題,再把這些問題的解決方案放回一起。帶代碼中也類似,主要考慮的問題如下:
(1)針對某個函數(shù)或代碼塊,思考這段代碼的高層次目標是什么?
(2)針對每一行代碼,思考其是否為了直接目標而工作,其高層次目標是什么?
(3)若由足夠的行數(shù)在解決不相關的子問題,抽取代碼到獨立的函數(shù)中。

也就是,把一般代碼和項目專有的代碼分開。使得最后大部分都是一般代碼,通過建立一大組庫和輔助函數(shù)來解決一般問題,剩下的只是讓程序與眾不同的核心部分。

純工具代碼

一些核心任務,大多數(shù)程序都會做,例如操作字符串、使用哈希表以及讀/寫文件。通常,這些“基本工具”是由編程語言中內(nèi)置的庫來實現(xiàn)的,對于沒有的方法,需要自己來完成空白,也就是一個不相關的子問題,并應抽取到一個新的函數(shù)中。比如:讀取文件函數(shù):ReadFileToString()。

創(chuàng)建大量通用代碼

在項目中廣泛適用,常常有個專門的目錄來存放這種代碼(例如util),方便重用。通用代碼有很多好處,完全從項目的其他部分中解耦出來,容易開發(fā),容易測試,并且容易理解。

簡化已有接口

創(chuàng)建自己的包裝函數(shù),隱藏不理想的接口。

過猶不及

為代碼增加一個函數(shù)存在一個小的(卻有形的)可讀性代價,要注意適度,根據(jù)項目需求,付出代價滿足得到成效才可以。

一次只做一件事

  1. 應該把代碼組織得一次只做一件事情,如果所有代碼糾纏在一起,對于每個任務都很難靠其自身來理解它從哪里開始,到哪里結(jié)束。
  2. 如果代碼很長很難理解,嘗試把其所做的所有任務列出來,其中一些任務可以很容易地變成單獨的函數(shù)(或類)。其他的可以簡單地成為一個函數(shù)中的邏輯“段落”。
  3. 難點:準確描述程序所做的小事情。

把想法編變成代碼

簡單技巧:用自然語言描述程序然后用這個描述來寫出更自然的代碼。所做的任務如下:

(1)像對著一個同事一樣用自然語言描述代碼要做什么    
(2)注意描述中所用    
(3)寫出與描述所匹配的代碼

具體的需要做到如下幾點:
1、清楚地描述邏輯
2、了解函數(shù)庫是有幫助的
3、把這個方法應用于更大的問題
(1)用自然語言描述解決方案
(2)遞歸使用(1)

少寫代碼

所寫的每一行代碼都是要測試和維護的,通過重用庫或者減少功能,可以節(jié)省時間并且使代碼庫保持精簡節(jié)約??梢酝ㄟ^以下幾種方法少寫代碼:

  1. 保持小代碼庫
    (1)創(chuàng)建越多越好的“工具”代碼來減少重復代碼
    (2)減少無用代碼或沒有用的功能
    (3)項目保持分開的子項目狀態(tài)
    (4)保持代碼又輕又靈
  2. 刪除獨立的函數(shù)很簡單,不讓無用代碼交織在項目中。
  3. 熟悉周邊的庫:每隔一段時間,花15分鐘來閱讀標準庫中的所有函數(shù)/模塊/類型的名字。
  4. 重用庫有著很多好處,不僅節(jié)省時間,而且少寫代碼。在一個成熟的庫中,每一行代碼都代表相當大量的設計、調(diào)試、重寫、文檔、優(yōu)化和測試。任何經(jīng)受了這樣達爾文進化過程一樣的代碼行都是很有價值的。

第二部分 測試與可讀性

測試:任何僅以檢查另一段代碼的行為為目的的代碼。

  1. 使測試易于閱讀和維護。
    測試代碼的可讀性和非測試代碼是同樣重要的,可以把測試代碼看做非正式的文檔,它記錄了真實代碼是如何工作和應該如何使用。測試應當具有可讀性,以便其他程序員可以舒服地改變或者增加測試。

  2. 測試原則:對使用者隱去不重要的細節(jié),以便更重要的細節(jié)會更突出。

  3. 測試基本內(nèi)容:對于這樣的輸入/情形,期望有這樣的行為/輸出。
    這個目的很多時候可以用一行代碼來表達,使代碼緊湊而又易讀,讓測試的表述保持很短還會讓增加測試變得很簡單。

  4. 讓錯誤消息具有可讀性:如果測試失敗了,所發(fā)出的錯誤消息應該能容易跟蹤并修正這個bug。

  5. 測試輸入:選擇一組最簡單的輸入,能完整的使用被測代碼。

  6. TDD測試驅(qū)動開發(fā)
    測試驅(qū)動開發(fā)是一種編程風格,在寫真實代碼之前就寫出測試。一般來講,如果在設計代碼時發(fā)現(xiàn),這對測試來說是噩夢,那么就應該重新考慮這個設計。

如表所示為可測性差的代碼的特征,以及它所帶來的設計問題:

特征 可測性的問題 設計問題
使用全局變量 對于每個測試都要重置所有的全局狀態(tài)(否則,不同的測試之間會相互影響) 很難理解哪些函數(shù)有什么副作用。沒辦法獨立考慮每個函數(shù),要考慮整個程序才能理解是不是所有的代碼都能工作
對外部組件有大量依賴的代碼 很難給它寫出任何測試,因為要先搭起太多的腳手架。寫測試會比較無趣,因此人們避免寫測試。 系統(tǒng)會更可能因某一依賴失敗而失敗。對于改動來講很難知道會產(chǎn)生什么樣的影響。很難重構類,并且要考慮更多恢復路勁
代碼又不確定的行為 測試會很古怪,而且不可靠。經(jīng)常失敗的測試最終會被忽略。 這種程序更可能會有條件競爭或者其他難以重現(xiàn)的bug。這種程序很難推理。產(chǎn)品中的bug很難跟蹤和改正。

另一方面,如果設計的代碼容易寫出測試,是個好現(xiàn)象。如下表所示為可測性較好的代碼的特征,以及它所產(chǎn)生的優(yōu)秀設計:

特征 對可測性的好處 對設計的好處
類中只有很少或者沒有內(nèi)部狀態(tài) 很容易寫出測試,因為測試一個方法只要較少的設置,并且有較少的隱藏狀態(tài)需要檢查 有較少狀態(tài)的類更簡單,更容易理解
類/函數(shù)只做一件事 要測試它只需要較少的測試用例 較小/較簡單的組件更加模塊化,并且一般來講系統(tǒng)有更少的耦合
每個類對別的類的依賴很少;低耦合 每個類可以獨立地測試(比多個類一起測試容易的多) 系統(tǒng)可以并行開發(fā)??梢院苋菀仔薷幕蛘邉h除類,而不會影響系統(tǒng)的其他部分
函數(shù)的接口簡單,定義明確 有明確的行為可以測試。測試簡單接口所需的工作量較少 接口更容易讓程序員學習,并且重用的可能性更大

小結(jié)

《編寫可讀代碼的藝術》這本書看完了,也總結(jié)了三篇讀書筆記,分別為編寫可讀代碼的藝術:初·代碼審美、編寫可讀代碼的藝術:次·循環(huán)邏輯優(yōu)化、編寫可讀代碼的藝術:末·代碼塊&測試。 這三篇文章中,對代碼的優(yōu)化的范圍越來越大,難度也越來越高,從基本的變量名字修改到循環(huán)邏輯優(yōu)化,再到本文中整個代碼塊的設計和測試代碼的可讀性,到這里時,已經(jīng)不是寥寥數(shù)語可以記錄的,知識點和理論很少,說起來也很簡單,我甚至把四大部分中的后兩部分合并寫做一篇博客,因為講起來真的很容易,但需要的是大量的實踐和總結(jié)。原來覺得一個高級程序員或者說有經(jīng)驗的程序員,就是代碼量很多,做過很多項目,現(xiàn)在才覺得,代碼的設計,項目的結(jié)構,合理的測試等等,這些才是更重要的。寫代碼這件事情,并不是表面看起來那么的體力活,或許每個程序員都應該把自己看做一個作家,雖然這個世界上有很多種語言,我們的目的不是熟練某一種語言,也不是僅僅能夠?qū)懗鐾暾摹白髌贰本涂梢粤?,更重要的,是寫出“藝術感”的“作品”。知識的海洋太過浩渺,世界上的聰明人數(shù)不甚數(shù),擼起袖子加油干,共勉。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容