讀《程式碼不朽:編寫可維護軟體的10大要則》C# 版

dotNET跨平臺發表於2017-06-12

640?wx_fmt=jpeg

這本書特別針對沒有接受過電腦科學或軟體工程專業學習的軟體開發人員,這類人員除了熟悉所用語言語法和語義之外,很少接受其他專業培訓,對軟體工程中的一些概念理解欠缺。軟體設計方面考慮較少。如果要成為一個專業的程式設計師,就需要學習已形成工業化的軟體構建方式。

可維護性解釋

這本書解釋了可維護軟體中的“維護”的意思:可維護性是軟體質量的一個標準,代表一個系統可被修改的難易程度。所以它是面向程式設計師的,假設兩個軟體完成相同的功能,但一個軟體的原始碼,讓其他人或者一段時間之後的自己,很難理解,更不用提修改了,就說明這個軟體的可維護性比另一個差。軟體維護有4種方式:發現並糾正bug(糾正性維護);適應作業系統或執行環境的改變(適應性維護);根據需求增加新的功能(完善性維護);改進程式碼質量預防bug產生(預防性維護)。

三個基本理論

本書提供了10條可以實現高可維護性的指導原則,這些原則背後有三個理論:

1、堅持簡單的原則最有助於提高可維護性

2、可維護性不是專案開發完後才去考慮的,而應該是在專案開發的一開始就加以考慮。每個人的貢獻都應當計算在內

3、不同原則的違例會帶來不同的影響,有些嚴重程度甚至於他。一個軟體系統越遵守原則,可維護性越高。

 

對10大要則的理解

按照從小到大,從細微到巨集觀的層次,這本書提取了編寫可維護軟體中10大程式設計原則,小到程式開發者應當時刻注意的程式碼規範,大到系統架構師應該考慮的系統重構、元件和及介面的設計準則。

編寫短小的程式碼單元

程式碼單元即物件導向程式設計裡的方法或函式。這個原則要求每個函式的長度不應超過15行。

動機

小的函式的好處?作者提出,小的函式容易重用,因為一個巨型的方法會包含很多細節,導致很難有一模一樣的場景使用這個方法。作者提出,小的方法更易理解和進行單元測試。若超過15行,則意味著方法可以被拆分了。

如何使用本原則

拆分重構的方式有提取方法將方法替換為方法物件
提取方法很容易理解,即從一個函式中提取一段程式碼,寫成一個新的方法。但如果提取方法時發現,這個方法訪問了很多區域性變數,如果都作為新方法的引數的話,勢必會導致引數列表過長。還有返回值的問題,如果這個方法會產生不止一個結果變數。一個重構技巧是將這個方法替換成一個方法物件,將不同的區域性變數和結果變數作為類的成員,然後呼叫類方法。

編寫簡單的程式碼單元

這裡的“簡單”體現程式碼單元的分支點,所以這個原則可量化為:限制每個程式碼單元分支點的數量不超過4個。C# 中常見的分支點程式碼就是if和switch語句。

動機

讓程式碼單元保持簡單基於兩個原因,一是簡單的程式碼更容易修改,二是簡單的程式碼更容易測試,分支點過多,意味著要有更多的測試用例。

如何使用本原則

複雜的程式碼單元可能是因為其中包含很多互不相關的程式碼塊,這種情況可以採用“提取方法”
若是其它複雜的情況,比如碰到鏈式的條件語句,如下判斷國旗的語句:

...
List<Color> result;
switch(nationality) {
  case CHINA:
    result= new List<Color>{Color.RED,Color.YELLOW};
    break;
  case FRENCH:
    result= new List<Color>{Color.BLUE,Color.WHITE,Color.RED};
    break;
...
}

第一種方法是引入Map資料結構,將國家對映到指定的FLAG物件上;
第二種方法是使用“使用多型來代替條件判斷”,實現同一個介面,代表廣泛的國旗型別,然後為每個國家的國旗實現一個類。
再比如碰到巢狀的條件語句,為了使程式碼簡單,可以使用“使用衛語句來代替巢狀的條件語句”的重構技巧,即標識出各種獨立的情況,並插入return語句來代替巢狀式的條件語句。
例如

if(...) {
  if(...) {
    //TODO CASE1
  } else {
    //TODO CASE2
  }
} else {
  //TODO CASE3
}

可以改寫成

if(...) {
  //TODO CASE1
  return;
}
if(...) {
  //TODO CASE2
  return;
}
if(...) {
  //TODO CASE3
  return;
}

可以看到分支點並未減少,然後可以再用“提取方法”減少複雜度。

不寫重複程式碼

對重複程式碼的定義是,一段至少6行都相同的程式碼。

動機

如果複製程式碼,相同的程式碼出現在不同的地方,不利於原始碼的定位;如果需要修改的地方正是重複的程式碼,意味著要做很多重複性的工作,而且容易出錯。

如何使用本原則

首先想到的是提取方法;但若是一個方法是另一個類的私有方法怎麼辦?這時應當將提取的方法放到一個工具類中。
如果重複程式碼(6行以上完全相同)已不存在,但程式碼相似,具有相同的邏輯,這時應該考慮提取父類。

保持程式碼單元的介面簡單

限制每個程式碼單元的引數不能超過4個。

動機

較少的介面引數能夠保持簡單的上下文,易於重用、理解和修改。

如何使用本原則

將多個引數包裝成物件,比如輸入座標引數,x與y,可以包裝成一個點物件。
使用“使用方法物件替換方法”的重構技巧,此處和前面有重合。

分離模組之間的關注點

模組對應類的概念。
實際上就是要求類要保持小的體積,不要過大過複雜。

動機

小的體積的類帶來了類之間的鬆耦合,鬆耦合意味著類能更靈活的適應將來的變化。如果一個類做了很多事情,其耦合度會越來越緊,積攢大量程式碼,導致程式碼很難閱讀和修改。

如何使用本原則

第一種方法:根據功能將大類拆分為很小的類。一個類一開始可能很小,只是實現單一功能,但都不可避免負責越來越多的職責,當意識到這個類承擔了不止一個職責時,就應該將這個類進行拆分。
第二種方法:提取一個介面,實現鬆耦合。比如一開始為一臺相機設計了簡單的相機類,只具備拍照,閃光燈開啟和關閉3個方法。後來這個類的使用擴充套件到新的移動裝置上,增加了定時功能。這時類變大,而且只有一個類,還需要檢查舊裝置上的程式碼有沒有受影響。為了降低耦合度,可以使用一個介面,它只定義所有相機都需要實現的功能。
第三種方法:使用第三方庫和框架來替代自定義的實現。

架構元件鬆耦合

元件是比模組(類)更高一層的單元,設計到系統的架構。此原則要求儘可能減少當前模組暴露給(例如,被呼叫)其它元件中模組的相關程式碼。

動機

獨立的元件可以單獨進行維護,方便劃分職責,讓測試變得容易。

如何使用本原則

使用抽象工廠設計模式,簡單的講就是類的例項不能直接被建立(new一個),而是通過工廠類的方法返回。這種通用的工廠介面背後,隱藏了具體產品的建立過程。在這個環境下,產品通常都不止有一種型別。如果要使用其中的邏輯,需要通過建立通用的工廠物件呼叫類方法成員。
注:抽象工廠不同於工廠模式,簡單理解就是抽象工廠的型別不止一個,所以產品至少有兩個。

保持架構元件之間的平衡

保持原始碼中的元件數量接近於9。

動機

好的元件平衡讓查詢和分析程式碼更容易,提供清晰的功能邊界,分離維護職責。

如何使用本原則

軟體系統的開發有兩種組織模式:
基於功能領域劃分的系統:好處是可以從高層功能的角度來分析程式碼,壞處是技術人員需要了解多個技術棧
基於技術劃分的系統:根據技術專長來劃分,可能會有前端,後端,介面、日誌等元件。
軟體架構師需要選擇如何組合功能的合適原則。明確系統的領域並堅持下去。

保持小規模程式碼庫

動機

大型系統更加難以維護,易出現更密集的缺陷,以大型程式碼庫為目標的專案更容易失敗。

如何使用本原則

功能層面:控制需求蔓延,功能標準化
技術層面:不要複製黏貼程式碼,重構程式碼,使用第三方庫和框架(這同樣是前面提到的準則)

自動化開發部署和測試

測試包含單元測試、整合測試、端對端測試、迴歸測試、驗收測試。不同型別的測試需要不同的自動化框架。

動機

自動化測試可重複,有效率;自動化測試裡的斷言(assert)可以充當註釋;通過編寫測試可以反過來推促編寫可測試的程式碼,提高程式碼質量。

如何使用本原則

使編寫單元測試成為每個開發人員的職責,比如使用C#中的單元測試框架Xunit.net。
使用像moq或者mocking這樣的技術。stub即測試樁。需要測試樁是因為有些影響測試結果的測試條件是易變、無法統一的。比如拍照,兩次拍攝的環境不可能完全相同,結果無法驗證,所以需要一個假物件,即測試樁。mocking(模擬)是因為測試中某些函式是沉默的,不包含任何結果,可以在函式中新增計數來驗證函式執行過。mock技術有自動化的框架。
建議生產程式碼和測試程式碼一比一,提高覆蓋率。

編寫簡潔的程式碼

給程式開發人員總結了7條“童子軍軍規”:
1、編寫單元級別的良好程式碼
2、不要編寫不好的註釋
3、不要註釋程式碼
4、不要保留廢棄程式碼
注:包括3,同時還有其它的形式,比如不可能執行到的程式碼、無用的私有方法、註釋中的程式碼
5、不要使用過長的識別符號名稱
6、不要使用魔術常量
注:指表示式中突兀出現的數字,應該先定義。
7、不要使用未正確處理的異常
注:包括以下情況,捕獲異常卻不處理(catch為空),直接捕獲通用異常(比如Exception異常,這些異常不會提供觸發失敗的狀態或事件資訊,所以沒意義),將異常資訊展示給終端使用者(避免使用者困惑或暴露資訊,應該先轉換為通用資訊)

原文地址:https://www.qcloud.com/community/article/545562


.NET社群新聞,深度好文,微信中搜尋dotNET跨平臺或掃描二維碼關注

640?wx_fmt=jpeg

相關文章