在亞馬遜的新員工(程序員)都被推薦要讀一讀這本書

在亞馬遜的新員工(程序員)都被推薦要讀一讀這本書

 

如果您想真正學習,最好的方法是閱讀Robert C Martin的Clean Code。基本上,它普遍推薦給亞馬遜的新員工。
中文版由人民郵電出版社異步社區出版,中文名:《代碼整潔之道》,作者:Robert,C,Martin。

在亞馬遜的新員工(程序員)都被推薦要讀一讀這本書

 

學寫整潔代碼很難。它可不止於要求你掌握原則和模式。你得在這上面花工夫。你須自行實踐,且體驗自己的失敗。你須觀察他人的實踐與失敗。你須看看別人是怎樣蹣跚學步,再轉頭研究他們的路數。你須看看別人是如何絞盡腦汁做出決策,又是如何爲錯誤決策付出代價。

閱讀本書要多用心思。這可不是那種降落前就能讀完的“感覺不錯”的飛機書。本書要讓你用功,而且是非常用功。如何用功?閱讀代碼——大量代碼。而且你要去琢磨某段代碼好在什麼地方、壞在什麼地方。在我們分解,而後組合模塊時,你得亦步亦趨地跟上。這得花些工夫,不過值得一試。

本書大致可分爲3個部分。前幾章介紹編寫整潔代碼的原則、模式和實踐。這部分有相當多的示例代碼,讀起來頗具挑戰性。讀完這幾章,就爲閱讀第2部分做好了準備。如果你就此止步,只能祝你好運啦!

第2部分最需要花工夫。這部分包括幾個複雜性不斷增加的案例研究。每個案例都清理一些代碼——把有問題的代碼轉化爲問題少一些的代碼。這部分極爲詳細。你的思維要在講解和代碼段之間跳來跳去。你得分析和理解那些代碼,琢磨每次修改的來龍去脈。

你付出的勞動將在第3部分得到回報。這部分只有一章,列出從上述案例研究中得到的啓示和靈感。在遍覽和清理案例中的代碼時,我們把每個操作理由記錄爲一種啓示或靈感。我們嘗試去理解自己對閱讀和修改代碼的反應,盡力瞭解爲什麼會有這樣的感受、爲什麼會如此行事。結果得到了一套描述在編寫、閱讀、清理代碼時思維方式的知識庫。

如果你在閱讀第2部分的案例研究時沒有好好用功,那麼這套知識庫對你來說可能所值無幾。在這些案例研究中,每次修改都仔細註明了相關啓示的標號。這些標號用方括號標出,如:[H22]。由此你可以看到這些啓示在何種環境下被應用和編寫。啓示本身不值錢,啓示與案例研究中清理代碼的具體決策之間的關係纔有價值。

如果你跳過案例研究部分,只閱讀了第1部分和第3部分,那就不過是又看了一本關於寫出好軟件的“感覺不錯”的書。但如果你肯花時間琢磨那些案例,亦步亦趨——站在作者的角度,迫使自己以作者的思維路徑考慮問題,就能更深刻地理解這些原則、模式、實踐和啓示。這樣的話,就像一個熟練地掌握了騎車的技術後,自行車就如同其身體的延伸部分那樣;對你來說,本書所介紹的整潔代碼的原則、模式、實踐和啓示就成爲了本身具有的技藝,而不再是“感覺不錯”的知識。


乾貨分享:第12章 迭進

12.1 通過迭進設計達到整潔目的

假使有4條簡單的規矩,跟着做就能幫助你創建優良的設計,會如何?假使遵循這些規矩你就能洞見代碼的結構和設計,更輕易地應用SRP和DIP之類原則,又會如何?假使這4條規則有利於良好的設計“浮現”出來,又會如何?

我們中的許多人認爲,Kent Beck關於簡單設計[1]的四條規則,對於創建具有良好設計的軟件有着莫大的幫助。

  • 據Kent所述,只要遵循以下規則,設計就能變得“簡單”:
  • 運行所有測試;
  • 不可重複;
  • 表達了程序員的意圖;
  • 儘可能減少類和方法的數量;
  • 以上規則按其重要程度排列。

12.2 簡單設計規則1:運行所有測試

設計必須製造出如預期一般工作的系統,這是首要因素。系統也許有一套絕佳設計,但如果缺乏驗證系統是否真按預期那樣工作的簡單方法,那就無異於紙上談兵。

全面測試並持續通過所有測試的系統,就是可測試的系統。看似淺顯,但卻重要。不可測試的系統同樣不可驗證。不可驗證的系統,絕不應部署。

幸運的是,只要系統可測試,就會導向保持類短小且目的單一的設計方案。遵循SRP的類,測試起來較爲簡單。測試編寫得越多,就越能持續走向編寫較易測試的代碼。所以,確保系統完全可測試能幫助我們創建更好的設計。

緊耦合的代碼難以編寫測試。同樣,編寫測試越多,就越會遵循DIP之類規則,使用依賴注入、接口和抽象等工具儘可能減少耦合。如此一來,設計就有長足進步。

遵循有關編寫測試並持續運行測試的簡單、明確的規則,系統就會更貼近OO低耦合度、高內聚度的目標。編寫測試引致更好的設計。

12.3 簡單設計規則2~4:重構

有了測試,就能保持代碼和類的整潔,方法就是遞增式地重構代碼。添加了幾行代碼後,就要暫停,琢磨一下變化了的設計。設計退步了嗎?如果是,就要清理它,並且運行測試,保證沒有破壞任何東西。測試消除了對清理代碼就會破壞代碼的恐懼

在重構過程中,可以應用有關優秀軟件設計的一切知識。提升內聚性,降低耦合度,切分關注面,模塊化系統性關注面,縮小函數和類的尺寸,選用更好的名稱,如此等等。這也是應用簡單設計後三條規則的地方:消除重複,保證表達力,儘可能減少類和方法的數量。

12.4 不可重複

重複是擁有良好設計系統的大敵。它代表着額外的工作、額外的風險和額外且不必要的複雜度。重複有多種表現。極其雷同的代碼行當然是重複。類似的代碼往往可以調整得更相似,這樣就能更容易地進行重構。重複也有實現上的重複等其他一些形態。例如,在某個羣集類中可能會有兩個方法:

int size() {}
boolean isEmpty() {}

這兩個方法可以分別實現。isEmpty方法跟蹤一個布爾值,而size方法則跟蹤一個計數器。或者,也可以通過在isEmpty中使用size方法來消除重複:

boolean isEmpty() {
 return 0 == size();
}

要想創建整潔的系統,需要有消除重複的意願,即便對於短短几行也是如此。例如以下代碼:

public void scaleToOneDimension(
  float desiredDimension, float imageDimension) {
 if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
  return;
 float scalingFactor = desiredDimension / imageDimension;
 scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);

 RenderedOp newImage = ImageUtilities.getScaledImage(
   image, scalingFactor, scalingFactor);
 image.dispose();
 System.gc();
 image = newImage;
}
public synchronized void rotate(int degrees) {
 RenderedOp newImage = ImageUtilities.getRotatedImage(
   image, degrees);
 image.dispose();
 System.gc();
 image = newImage;
}

要保持系統整潔,應該消除scaleToOneDimension和rotate方法裏面的少量重複:

public void scaleToOneDimension(
  float desiredDimension, float imageDimension) {
 if (Math.abs(desiredDimension - imageDimension) < errorThreshold)
  return;
 float scalingFactor = desiredDimension / imageDimension;
 scalingFactor = (float)(Math.floor(scalingFactor * 100) * 0.01f);
 replaceImage(ImageUtilities.getScaledImage(
   image, scalingFactor, scalingFactor)); 
}

public synchronized void rotate(int degrees) {
 replaceImage(ImageUtilities.getRotatedImage(image, degrees)); 
}

private void replaceImage(RenderedOp newImage) {
 image.dispose();
 System.gc();
 image = newImage;
}

做了一點點共性抽取後,我們意識到已經違反了SRP原則。所以,可以把一個新方法分解到另外的類中,從而提升其可見性。團隊中的其他成員也許會發現進一步抽象新方法的機會,並且在其他場景中複用之。“小規模複用”可大量降低系統複雜性。要想實現大規模複用,必須理解如何實現小規模複用。

模板方法模式[2]是一種移除高層級重複的通用技巧。例如:

public class VacationPolicy {
 public void accrueUSDivisionVacation() {
  // code to calculate vacation based on hours worked to date
  // ...
  // code to ensure vacation meets US minimums
  // ...
  // code to apply vaction to payroll record
  // ...
 }
 public void accrueEUDivisionVacation() {
  // code to calculate vacation based on hours worked to date
  // ...
  // code to ensure vacation meets EU minimums
  // ...
  // code to apply vaction to payroll record
  // ...
 }
}

除了計算法定最少數量假期的部分,accrueUSDivisionVacation和accrueEuropeanDivision Vacation中有大量代碼雷同。那部分的算法,依據員工類型而變。

可以通過應用模板方法模式來消除明顯的重複。

abstract public class VacationPolicy {
 public void accrueVacation() {
  calculateBaseVacationHours(); 
  alterForLegalMinimums(); 
  applyToPayroll(); 
 }

 private void calculateBaseVacationHours() { /* ... */ };
 abstract protected void alterForLegalMinimums();
 private void applyToPayroll() { /* ... */ };
}

public class USVacationPolicy extends VacationPolicy {
 @Override protected void alterForLegalMinimums() {
  // US specific logic
 }
}

public class EUVacationPolicy extends VacationPolicy {
 @Override protected void alterForLegalMinimums() {
  // EU specific logic
 }
}

子類填充了accrueVacation算法中的“空洞”,提供不重複的信息。

12.5 表達力

我們中的大多數人都經歷過費解代碼的糾纏。我們中的許多人自己就編寫過費解的代碼。寫出自己能理解的代碼很容易,因爲在寫這些代碼時,我們正深入於要解決的問題中。代碼的其他維護者不會那麼深入,也就不易理解代碼。

軟件項目的主要成本在於長期維護。爲了在修改時儘量降低出現缺陷的可能性,很有必要理解系統是做什麼的。當系統變得越來越複雜,開發者就需要越來越多的時間來理解它,而且也極有可能誤解。所以,代碼應當清晰地表達其作者的意圖。作者把代碼寫得越清晰,其他人花在理解代碼上的時間也就越少,從而減少缺陷,縮減維護成本。

可以通過選用好名稱來表達。我們想要聽到好類名和好函數名,而且在查看其權責時不會大喫一驚。

也可以通過保持函數和類尺寸短小來表達。短小的類和函數通常易於命名,易於編寫,易於理解。

還可以通過採用標準命名法來表達。例如,設計模式很大程度上就關乎溝通和表達。通過在實現這些模式的類的名稱中採用標準模式名,例如COMMAND或VISITOR,就能充分地向其他開發者描述你的設計。

編寫良好的單元測試也具有表達性。測試的主要目的之一就是通過實例起到文檔的作用。讀到測試的人應該能很快理解某個類是做什麼的。

不過,做到有表達力的最重要方式卻是嘗試。有太多時候,我們寫出能工作的代碼,就轉移到下一個問題上,沒有下足功夫調整代碼,讓後來者易於閱讀。記住,下一位讀代碼的人最有可能是你自己。

所以,多少尊重一下你的手藝吧。花一點點時間在每個函數和類上。選用較好的名稱,將大函數切分爲小函數,時時照拂自己創建的東西。用心是最珍貴的資源。

12.6 儘可能少的類和方法

即便是消除重複、代碼表達力和SRP等最基礎的概念也會被過度使用。爲了保持類和函數短小,我們可能會造出太多的細小類和方法。所以這條規則也主張函數和類的數量要少。

類和方法的數量太多,有時是由毫無意義的教條主義導致的。例如,某個編碼標準就堅稱應當爲每個類創建接口。也有開發者認爲,字段和行爲必須切分到數據類和行爲類中。應該抵制這類教條,採用更實用的手段。

我們的目標是在保持函數和類短小的同時,保持整個系統短小精悍。不過要記住,這在關於簡單設計的四條規則裏面是優先級最低的一條。所以,儘管使類和函數的數量儘量少是很重要的,但更重要的卻是測試、消除重複和表達力。

12.7 小結

有沒有能替代經驗的一套簡單實踐手段呢?當然不會有。另一方面,本章中寫到的實踐來自於本書作者數十年經驗的精練總結。遵循簡單設計的實踐手段,開發者不必經年學習就能掌握好的原則和模式。

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