代碼質量隨想錄(一)可讀是王道

  一直以來想寫點關於代碼質量的心得,礙於自身的懶惰。今天終究找到一個提前忙完工作的午後,可以先讓自己的思路開動起來了。

  最終促使我開始整理自己對於代碼質量的看法,還多虧了前陣子認識的Long小朋友,他及時地向我推薦了《The Art of Readable Code》這本書(下文簡稱ARC)。在看過了馬叔叔的《Clean Code》和《Clean Coder》之後,這本書徹底讓我沉迷於代碼質量之中了。

  我就將每天讀書所做的筆記和自己的想法綜合起來,再加上原來歷次項目之中的研究心得,陸續寫出來,與大家分享。也歡迎更多的代碼潔癖者一起交流。

  可讀性一直是代碼質量管控所追求的目標之一。沒有這個,後面的可維護和可修改都不太容易達成。可讀性怎麼強調都不爲過,ARC一書的作者對具備可讀性的代碼給出說法是:

  花最少的時間就能理解的代碼。

  私以爲這個定義是我暫時能找到的比較合理的解釋了。可讀性的研究應該從橫向與縱向兩個層面展開。

  橫向地說,團隊內部,尤其是開源項目,更是要維護個成員之間對代碼的理解度。封裝與理解並不矛盾,封裝是爲了更好的讓客戶代碼理解其結構與功能,更爲恰當地使用這個模塊。僥倖將自己搞不清爽的代碼以封裝的名義塞到某個莫名其妙的類中,終歸會導致那部分代碼的缺陷通過接口或頻繁使用暴露出來。這一點是個大問題,以後專文再述。在同一個項目或者同一個模塊工作的開發者之間一定要彼此理解對方所寫的代碼。這就是我爲何一再強調“交叉代碼評審”的原因。爲了減少橫向溝通的時間,提高合作效率,大家最好是在一份理解地比較透徹的代碼庫上進行協作。如果發現某段代碼出現難於理解的情況,立即自我檢查、並於其他同事討論,大家一起拿出來個所有人都易於理解的辦法來。切勿打出“時間緊,以後再說”(一旦說出這種話,我還沒見過以後還有人會主動回過頭來整理代碼)的擋箭牌或者“勿要過分偏執於代碼質量”這樣的理由。實際上,很多工作中的溝通不暢都是源於對產品代碼、設計、架構的理解不到位。對於這個問題,我提倡採用極限編程或與其等效的協同式結對或組團工作法,同時縮短代碼評審週期。所有一線程序員一定要經常舉行20-25分鐘左右(番茄法)的技術討論對話。

  縱向地說,代碼的理解實際上也是對程序員本人業務能力的一種拓展訓練。在沒有系統地學習敏捷開發等代碼管控技術之前,我經常對自己幾個月前、幾周前甚至三天前所寫的代碼一頭霧水,根本不清楚當時是在何種情境下寫出那些代碼的。協同工作時更爲嚴重,我們不僅要理解自己很久以前寫過的代碼,還要理解其他同事甚至離職人員的遺留代碼。如果不及時進行品質管控,整個項目就無法繼續健康的運作下去,因爲對當前模塊的編寫勢必要引入原來的既有模塊,而且爲了應對複雜多變的需求,必須經常把原來的代碼拿出來曬太陽,以便理順思路,儘速應對需求。長時間進行有意識的質量訓練,就可以在工作中積累大量的代碼範式和可複用模塊,並且在遇到新工作的新需求時及時從腦中呼出原有的高品質解決方案。

  在談到對“理解”的判定標準時,ARC的作者提出的標準也比較有參考性。他們認爲,代碼閱讀者能夠對其作出修改、能夠指出其中的Bug、能夠理解它與其餘部分代碼是如何進行溝通的。做到了上述這些,纔算“完全理解了代碼”。小翔我雅以爲是。在進行上述我提到的橫向和縱向溝通時,都必須以“徹底”理解爲溝通目標,不要矇混過去。

  除了橫向和縱向的溝通問題之外,還有一個問題就是如何處理代碼質量與其他工程要素之間的關係,例如代碼執行效率、軟件設計與架構、代碼是否易於測試等等。很多反對花時間提升代碼質量的人都拿這些來做文章。不過依我在實際工作中的感覺是,如果因爲代碼品質得不到保證而導致溝通不暢,那麼相應的效率、架構、易測試性都可能隨之出現問題,因爲它們最終都要落實到具體代碼與具體開發者身上,一個尊崇易讀性的編碼環境才能催生執行高效、架構合理、易於測試的代碼

  原來我之所以沒有及時將代碼質量的相關心得與想法總結起來,很重要的一個原因是代碼質量所涉及的知識點太多、太散,而且和其他話題聯繫頗多。在開始讀ARC這本書之後,我決定依照可讀性爲主線,把我這個有代碼潔癖者的所思所想整理成系列文章,這樣更方便按照主題去閱讀、研究。

  說到到可讀性的具體判定標準,這則是要靠每個人在學習、工作中不斷總結出來的。很多教材整本書所講的就是如何依據一系列的經驗法則來指導編程,比如《Clean Code》。我以爲不妨按照代碼層級,將可讀性的研究分爲“零散代碼改觀”、“簡化邏輯與循環”、“宏觀結構重整”三個部分。零散代碼改觀涉及函數或方法內部的命名與註釋等僅涵蓋數行代碼的初階問題。而邏輯與循環則是函數或方法代碼中的核心部分,它們通常以代碼塊或數行與流程相關的代碼組成。針對此部分的品質提升,主要表現在梳理控制流、簡化表達式、考究循環控制變量等問題。結構重整就是在更爲宏觀的函數、類、包等級別上進行質量管控。

  爲了說明代碼質量不是隨心所欲能決定的,我就翻炒一下ARC中的幾個小例子(小栗子)。

  跟着感覺走,有時不可靠。

  1. Node node = list.head; 
  2. if (node == null
  3.   return
  4. while (node.next != null) { 
  5.   print(node.data); 
  6.   node = node.next; 
  7. if (node != null)  
  8.   print(node.data);

  這段代碼當然不如下面這段簡潔,這大家憑感覺就能看出來:

  1. for (Node node = list.head; node != null; node = node.next) 
  2.   print(node.data);

  然而

  1. return exponent >= 0 ? mantissa * (1 << exponent) : mantissa / (1 << -exponent);

  與

  1. if (exponent >= 0) { 
  2.   return mantissa * (1 << exponent); 
  3. else { 
  4.   return mantissa / (1 << -exponent); 
  5.  

  誰好誰壞就難說了。第一個更簡潔,第二個更具親和力。

  短代碼未必不好

  1. assert((!(bucket = findBucket(key))) || !bucket.isOccupied());

  上一段代碼的可讀性不如下一段:

  1. bucket = findBucket(key); 
  2. if (bucket != nullassert(!bucket.isOccupied());

  多寫點註釋也好

  1. // 更快地執行"hash = (65599 * hash) + c" 
  2. hash = (hash << 6) + (hash << 16) - hash + c;

  上面這段代碼多虧了這個註釋,否則立刻滑如雜技代碼的深淵。

  下幾篇系列文章將講述如何選取易讀的標識符名稱。

愛飛翔

2012年5月31日

本文使用Creative Commons BY-NC-ND 3.0協議(創作共用 自由轉載-保持署名-非商業使用-禁止衍生)發佈。

原文網址:http://agilemobidev.net/eastarlee/code-quality/think_in_code_quality_1_readablity_zh_cn/

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