1. 程式人生 > >程式碼整潔之道精華——第十七章 味道與啟發

程式碼整潔之道精華——第十七章 味道與啟發

閱讀本文有兩種原因:第一,你是個程式設計師;第二,你想成為更好的程式設計師。你如果想成為更好的程式設計師,那就請細細品味文章內容,它絕不會讓你失望。
程式碼整潔之道教給大家如何編寫整潔的程式碼,而不僅僅是能執行的程式碼,這對於程式設計者而言很重要。我在讀這本書的第一遍時沒什麼感覺,但在讀第二遍時覺得它確實挺不錯的,如果有機會的話我會讀第三遍。下面是我在讀書過程中摘錄的精華內容,希望大家認真對待。各位看官如果讀完本文覺得書中的精華內容挺合自己的胃口,那就可以抽出時間認真地讀一下這本書。

1、程式碼應該有正確行為。這話看似明白,問題是我們很少能明白正確的行為有多複雜。開發著常常寫出他們以為能工作的函式,信賴自己的直覺,而不是努力去證明程式碼在所有的角落和邊界情況下真能工作。
沒有什麼可以代替謹小慎微。每種邊界條件、每種極端情形、每個異常都代表了某種可能搞亂優雅而直白的演算法的東西。別信賴直覺,追蹤每種邊界條件,並編寫測試。
2、建立分離較高層級一般性概念與較低層級細節概念的抽象模型,這很重要。有時我們建立抽象類來容納較高層級概念,建立派生類來容納較低層級概念。這樣做的時候,需要確保分離完整。所有較低層級概念放在派生類中,所有較高層級概念放在基類中。
這條規則對於原始檔、元件和模組也適用。良好的軟體設計要求分離位於不同層級的概念,將他們放到不同的容器中。有時這些容器是基類或派生類,有時是原始檔、元件或模組。無論哪種情況,分離都要完整,較低層級概念和較高層級概念不應混在一起。
3、如果看到基類提到派生類的名稱,就可能發現了問題。通常來說,基類對派生類應該一無所知。
當然也有例外,有時派生類數量嚴格固定,而基類中擁有在派生類之間選擇的程式碼。在有限狀態機的實現中這種情形很多見,在那種情況下,派生類和基類緊密耦合,總是在同一個jar檔案中部署。一般情況下,我們會把派生類和基類部署到不同的jar檔案中,確保基類jar檔案對派生類jar檔案的內容一無所知,我們就能把系統部署為分散和獨立的元件。修改了這些元件時,不必重新部署基元件就能部署它們。這意味著修改產生的影響極大地降低了,而維護系統也變得更加簡單。
4、設計良好的模組有著非常小的介面,讓你事半功倍。設計低劣的模組有著廣闊、深入的介面,讓你不得不事倍功半。設計良好的介面並不提供許多需要依靠的函式,所以耦合度也較低。設計低劣的介面提供大量你必須呼叫的函式,耦合度較高。
優秀的軟體開發人員應該學會限制類或模組中暴露的介面數量。類中的方法越少越好,函式知道的變數越少越好,類擁有的實體變數越少越好。
隱藏你的資料,隱藏你的工具函式,隱藏你的常量和臨時變數。不要建立擁有大量方法或大量實體變數的類,不要為子類建立大量受保護的變數和函式。盡力保持介面緊湊,通過限制資訊來控制耦合度。
5、類的方法只應對其所屬類中的變數和函式感興趣,不該垂青其他類中的變數和函式。當方法通過某個其他物件A的訪問器和修改器來操作A物件的內部資料,則它就依戀於A物件所屬類的範圍。他期望自己在那個類裡面,這樣就能直接訪問他操作的變數。
6、選擇運算元引數只是一種避免把大函式切分為多個小函式的偷懶做法。使用多個函式,通常好過向單個函式傳遞某些程式碼來選擇函式行為。
7、函式名稱應該表達其行為。
8、用命名常量代替迷之數字。
9、用多型替代if/else或switch/case
10、如果沒有if或while語句的上下文,布林邏輯就難以理解。應該把解釋了條件意圖的函式抽離出來。
例如:if(shouldBeDeleted(timer))要好於if(timer.hasExpired()&&timer.isRecurrent())
11、否定式要比肯定式難明白一些,所以儘可能將條件表示為肯定形式。
例如:if(buffer.shouldCompact())要好於if(!buffer.shouldNotCompact())
12、函式只該做一件事,函式只該做一件事,函式只該做一件事。
13、捕魚之前先織網,織網之前先編繩,這三項活動的執行順序(時序耦合)很重要。在程式中,每個函式都產生出下一個函式所需的結果,這樣一來就沒有理由不按順序呼叫函數了。
14、封裝邊界條件
邊界條件難以追蹤,把處理邊界條件的程式碼集中到一處,不要散落於程式碼中。我們不想四處見到+1和-1字樣。
if(level+1 < tage.length){
  parts = new Parse(body,tage,level+1,offset+endTag);
  body = null;
}
注意level+1出現了2次,這是個應該封裝到名為nextLevel之類的變數中的邊界條件。
int nextLevel = level+1;
if(nextLevel < tage.length){
  parts = new Parse(body,tage,level+1,offset+endTag);
  body = null;
}
15、函式應該只在一個抽象級上
函式中的語句應該在同一抽象層級上,該層級應該是函式名所示操作的下一層。
16、名稱的長度應與作用範圍的廣泛度相關。對於較小的作用範圍,可以用很短的名稱,而對於較大的作用範圍就該用較長的名稱。
17、整潔程式碼並非遵循一套規則寫就,學習一系列啟發並不足以讓你成為軟體匠人,專業性和技藝來自於驅動規程的價值觀。

拋開所有細節不談,程式碼整潔之道總體來說可以分為以下7點:

  • 執行所有測試
  • 減少重複程式碼
  • 提高表達力
  • 提早構建簡單抽象發
  • 類和方法都只做好一件事
  • 儘量減少類和方法的數量
  • 努力,讓營地比你來時更乾淨。努力,讓世界比你來時更乾淨。努力,讓程式碼比你簽出時更乾淨。