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

>>>  技術話題—商業文明的嶄新時代  >>> 簡體     傳統

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

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

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

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

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

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

  橫向地說,團隊內部,尤其是開源項目,更是要維護個成員之間對代碼的理解度。封裝與理解并不矛盾,封裝是為了更好的讓客戶代碼理解其結構與功能,更為恰當地使用這個模塊。僥幸將自己搞不清爽的代碼以封裝的名義塞到某個莫名其妙的類中,終歸會導致那部分代碼的缺陷通過接口或頻繁使用暴露出來。這一點是個大問題,以后專文再述。

  在同一個項目或者同一個模塊工作的開發者之間一定要彼此理解對方所寫的代碼。這就是我為何一再強調“交叉代碼評審”的原因。為了減少橫向溝通的時間,提高合作效率,大家最好是在一份理解的比較透徹的代碼庫上進行協作。如果發現某段代碼出現難于理解的情況,立即自我檢查、并于其他同事討論,大家一起拿出來個所有人都易于理解的辦法來。切勿打出“時間緊,以后再說”(一旦說出這種話,我還沒見過以后還有人會主動回過頭來整理代碼)的擋箭牌或者“勿要過分偏執于代碼質量”這樣的理由。

  實際上,很多工作中的溝通不暢都是源于對產品代碼、設計、架構的理解不到位。對于這個問題,我提倡采用極限編程或與其等效的協同式結對或組團工作法,同時縮短代碼評審周期。所有一線程序員一定要經常舉行20-25分鐘左右(番茄法)的技術討論對話。

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

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

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

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

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

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

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

Node node = list.head;
if (node == null)
  return;
while (node.next != null) {
  print(node.data);
  node = node.next;
}
if (node != null)
  print(node.data);

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

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

  然而

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

  與

if (exponent >= 0) {
return mantissa * (1 << exponent);
} else {
return mantissa / (1 << -exponent);
}

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

  短代碼未必不好

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

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

bucket = findBucket(key);
if(bucket !=null)assert(!bucket.isOccupied());

  多寫點注釋也好

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

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

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

  代碼質量隨想錄(二):必也正名乎

  代碼質量隨想錄(三):名字好,誤會少

  代碼質量隨想錄(四):排版,不只是為了漂亮

 


愛飛翔 2013-05-07 08:47:37

[新一篇] 想爬得更高,需要知道的數學體系

[舊一篇] 1985年我是如何寫代碼的
回頂部
寫評論


評論集


暫無評論。

稱謂:

内容:

驗證:


返回列表