讓我們先來看看程式設計大師 Robert C. Martin 的傑作《Clean Code》裡的一句話:
這句話可以簡單的理解為如果你的程式碼需要註釋,最有可能是你的程式碼寫的很爛。同樣,如果在沒有註解的情況下你無法用程式碼完整的表達你對一個問題或一個演算法的思路,那這就是一個失敗的訊號。最終,這意味著你需要用註解來闡明一部分的思想,而這部分在程式碼裡是看不出來的。好的程式碼能夠讓任何人在不需要任何註解的情況下看懂。好的編碼風格能將所有有助於理解這個問題的所有資訊都蘊含在程式碼裡。
在程式理論中,有一個概念叫做「自我描述的原始碼」。對於一段程式碼,一種常見的自我描述機制是遵循某種非嚴格定義的變數、方法、物件命名規則。這樣做的主要作用就是讓原始程式碼更易讀易懂。所以,也就更容易維護和擴充。
這篇文章裡,我將舉出一些例子,說明什麼是“不好的代碼”,什麼是“清楚的代碼”
命名要能揭示意圖
如何命名,在程式設計中這永遠都是個老大難問題。有些程式設計師喜歡簡化、縮短或加密名稱,使得只有他們自己能懂。下面讓我們來看一些例子:
不好的程式碼: int d;// 天數 int ds;int dsm;int faid; 「d」可以表示任何東西。作者使用註釋來表明他的意圖,卻沒有選擇用代碼來表示。而“faid”很容易導致誤解為ID。
清楚的程式碼: int elapsedTimeInDays;int daysSinceCreation;int daysSinceModification;int fileAgeInDays;;
命名時避免意義引起誤解的訊息
錯誤的訊息比沒有訊息更糟糕。有些程式設計師喜歡「隱藏」一些重要訊息,有時候他們也會寫出一些讓人誤解的程式碼。
不好的程式碼:
Customer[] customerList;Table theTable;
變數「customerList」其實不是個 list。它是一個普通的 array (或客戶集合)。除此之外,「theTable」是一個 Table 類型的物件(你可以用 IDE 容易的發現它的類型),「the」這個字是個不必要的干擾。
清楚的程式碼:
Customer[] customers;Table table;
命名要有適當的長度
在高階程式語言中,變數名稱的長度通常較不限制。變數名幾乎可以任何長度。雖然如此,這也可能使程式碼變得鬧心。
不好的程式碼:
var theCustomersListWithAllCustomersIncludedWithoutFilter;var list; 好的名稱應該只含有必要的字彙來表示一個概念。任何不必要的字詞都會使名稱變長、難於理解。名稱越短越好,前提是能在上下文中表達完整的意思(下訂單這個場景中,“customersInOrder” 要比 “list” 好)。
清楚的程式碼: var allCustomers;var customersInOrder;命名時編碼規範保持一致,讓規範幫助理解代碼
所有的程式設計技術(語言)都有自己的“風格”,稱為編碼規格。程式設計師應該在寫程式碼時遵循這些習慣,因為其他的程式設計師也知道這些,並且以這種風格編寫。下面我們來看一個沒有明顯規範的不好的程式碼例子。下面的這段程式碼並沒有遵循很好的已知的「編碼規範」(例如 PascalCase, camelCase, Hungarian 規範)。更糟的是,這有一個毫無意義的 bool 變數「change」。這是動詞(用來描述動作),但這裡的 bool 值是來描述一個狀態,所以,這裡應該用一個形容詞比較合適。
不好的程式碼: const int maxcount = 1bool change = truepublic interface Repositoryprivate string NAMEpublic class personaddressvoid getpublic interface Repositoryprivate string NAMEpublic class personaddressvoid getpublics() 一段程式碼,只看它的一部分,你就應該直接明白它是什麼類型,只需要看它的命名方法。
例如:你看到了“_name”,你就能知道它是私有變數。你應該在任何地方都利用這種表示方法,沒有例外情況。
清楚的程式碼: const int MAXCOUNT = 1bool isChanged = truepublic interface IRepositoryprivate string _namepublic class PersonfaceAddress命名時相同的概念用相同的字表達
定義概念很難。在軟體開發過程中,很多時間都花在分析業務場景、思考正確的定義裡面所有的元素。這些概念永遠都是讓程式設計師頭痛的事。
不好的程式碼://1. void LoadSingleData ()void FetchDataFiltered ()Void GetAllData ()//2. void SetDataToView (); (int value)
首先:
代碼的作者試圖表達“get the data”的概念,他使用了多個詞“load”,“getch”,“get” 。一個概念只用一個字表達就行了(在同一個場景中)。
第二:
“set”這個字用在了2 個概念裡:第一是“data loading to view”,第二個是「setting a value of object」。這是兩個不同的概念,你應該使用不同的字。
清楚的程式碼:
//1. void GetSingleData ()void GetDataFiltered ()Void GetAllData ()//2. void LoadDataToView ();void SetObjectValue (int value)命名時使用跟業務領域相關的字
程式設計師寫的所有程式碼都是跟業務領域場景邏輯相連的。為了讓所有關係到這個問題的人都能更好的理解,程式中應該使用在領域環境中有意義的名稱。
不好的程式碼: public class EntitiesRelation{Entity o1;Entity o2;} 當在編寫針對某個領域的程式碼時,你應該始終考慮使用領域有聯繫的名稱。在將來,當另外一個人(不僅是程式設計師,也許是測試人員)接觸你的程式碼時,他能輕鬆的理解這個業務領域裡你的程式碼是什麼意思(不需要業務邏輯知識)。你首先考慮的應該是業務問題,之後才是如何解決。
清楚的程式碼:
public class ProductWithCategory{Entity product;Entity category;} 命名時使用在特定環境裡有意義的字
程式碼裡名稱都有自己的上下文。上下文對於理解一個名稱非常重要,因為它能提供額外的資訊。讓我們來看看一個典型的「地址」上下文:
不好的程式碼:
string addressCity;string addressHomeNumber;string addressPostCode; 在大多數情況中,「Post Code」通常是地址的一部分,很顯然,郵遞區號不能單獨使用(除非你是在開發一個專門處理郵編的應用)。所以,沒有必要在「PostCode」的前面加上「address」。更重要的,所以的這些資訊都有一個上下文容環境,一個命名空間,一個類別。
在物件導向程式設計中,這裡應該用一個「Address」類別來表示這個位址資訊。
清楚的程式碼: class Address{string city;string homeNumber;string postCode;}命名方法總結
概述起來,做為程式設計師,你應該:
· 命名是來表達概念的 · 命名是來表達概念的
· 注意名稱長度,名稱裡只該含有必要的詞語
· 編碼規範有助於理解代碼,你應該使用它
· 名稱不要混用
· 名稱裡要有意義,在上下文裡有意義 由於上傳附件及文字限制,有時部分圖片、文字可能顯示不了,詳情請見:http://mp.weixin.qq.com/s?__biz=MzI5ODI3NzY2MA==&mid=10000563&idx=2&sn=528dc490ec0d000b8 > 歡迎大家一起交流。 掃描以下二維碼,獲取更多更精美文章! (掃碼關注有意不到的驚喜的哦!!) 關注我們微信訂閱號碼( uniguytech100) 與服務號碼(uniguytech),獲取更多更精美文章! 也歡迎加入【大家技術網討論QQ群】,群號:256175955,請備註你個人的介紹!讓我們一起聊聊it的那些事吧! |