按月彙整:五月 2012

代码质量随想录(一)可读是王道

  一直以来想写点关于代码质量的心得,碍于自身的懒惰。今天终究找到一个提前忙完工作的午后,可以先让自己的思路开动起来了。

  最终促使我开始整理自己对于代码质量的看法,还多亏了前阵子认识的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;

  上面这段代码多亏了这个注释,否则立刻滑入杂技代码的深渊。

  下几篇系列文章将讲述如何选取易读的标识符名称。

爱飞翔

2012年5月31日

本文使用Creative Commons BY-NC-ND 3.0协议(创作共用 自由转载-保持署名-非商业使用-禁止衍生)发布。
原文网址:http://agilemobidev.net/eastarlee/code-quality/think_in_code_quality_1_readablity_zh_cn/

廣告

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

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

  最終促使我開始整理自己對於代碼質量的看法,還多虧了前陣子認識的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;

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

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

愛飛翔

2012年5月31日

本文使用Creative Commons BY-NC-ND 3.0協議(創作共用 自由轉載-保持署名-非商業使用-禁止衍生)發佈。
原文网址:http://agilemobidev.net/eastarlee/code-quality/think_in_code_quality_1_readablity_zh_tw/