編碼習慣之命名規範
選擇一個好的命名
命名有兩個目標:
- 清晰:你要知道該命名與什麼有關
- 精確:你要知道該命名與什麼無關
當一個命名完成上面兩個目標之後,其餘的字元就是多餘的了。下面是我在開發時的一些命名原則:
命名中無需含有表示變數或引數型別的單詞
如果使用如Java之類的靜態型別語言,開發者通常知道變數的型別。由於方法的實現一般都比較簡短,所以即便是在檢視一個需要推斷才知道型別的本地變數,或者在code review等靜態分析器不可用的情況下,我們也可以通過多看很少的幾行程式碼就能知道變數的型別。
所以將型別說明加入到變數名中是多餘的。我們應該捨棄匈牙利命名法,如下:
// 不好的: String nameString; DockableModelessWindow dockableModelessWindow;// 改進: String name; DockableModelessWindow window;
特別是對於集合來說,最好使用名詞的複數形式來描述其內容,而不是使用名詞的單數形式來描述。如果開發者更在乎集合中儲存的內容, 那麼變數命名應當反映這一點。
// 不好的: List<DateTime> holidayDateList; Map<Employee, Role> employeeRoleHashMap; // 改進: List<DateTime> holidays; Map<Employee, Role> employeeRoles;
這一點也同樣適用於方法的命名。方法名不需要描述它的引數及引數的型別——引數列表已經說明了這些。
// 不好的: mergeTableCells(List<TableCell> cells) sortEventsUsingComparator(List<Event> events, Comparator<Event> comparator) // 改進: merge(List<TableCell> cells) sort(List<Event> events, Comparator<Event> comparator)
省略命名中不是用來消除歧義的單詞
有些開發者傾向於將他們知道的有關這個變數的所有資訊都塞到命名裡。要記住,命名只是一個識別符號:只是告訴你該變數是在哪定義的。並不是用來告訴閱讀者所有他們想知道的有關這個物件的詳細資訊。這是定義應該做的事情的。 命名只是讓你找到它的定義。
當我看到一個叫recentlyUpdatedAnnualSalesBid
(最近更新的全年銷售投標)的識別符號時,我會問:
- 存在不是最近更新的全年銷售投標麼?
- 存在沒有被更新的最近的全年銷售投標麼?
- 存在最近更新的非全年的銷售投標麼?
- 存在最近更新的全年非銷售的投標麼?
- 存在最近更新的全年銷售非投標的東東嗎?
上面任何一個問題的回答是“不存在”,就意味著命名中引入了無用的單詞。
// 不好的: finalBattleMostDangerousBossMonster; weaklingFirstEncounterMonster; // 改進: boss; firstMonster;
當然,你可能會覺得這有一些過了。比如將第一個例子的識別符號簡化為bid,
會讓人覺得有點模糊不清。但你可以放心大膽的這樣做,如果在之後的開發中覺得該命名會造成衝突或不明確,可以新增些修飾詞來完善它。反之,如果一開始就取了一個很長的命名,你是不可能在之後重新回來簡化它的。
省略命名中可以從上下文獲取的單詞
我可以在文章中使用”我”,因為讀者都知道這是一篇由Bob Nystrom所做的部落格。我蠢萌的臉就掛在那,我無需不停的說我的名字。寫程式碼也是一樣,類中的方法/屬性和方法中的變數,都是存在在上下文中的,無需重複。
// Bad: class AnnualHolidaySale { int _annualSaleRebate; void promoteHolidaySale() { ... } } // Better: class AnnualHolidaySale { int _rebate; void promote() { ... } }
實際上,一個命名巢狀的層次越多, 它就有更多的相關的上下文,也就更簡短。換句話說,一個變數的作用域越小,命名就越短。
省略命名中無任何含義的單詞
我常常在許多遊戲開發中看到包含無任何含義的單詞的命名,一些開發者喜歡在命名中新增一些看起來有點嚴肅的單詞。我猜可能他們覺得這樣做可以讓他們的程式碼顯得重要,或者說讓他們覺得自己更重要。
實際上,有一些詞語並沒有實際意義,只是一些套話。比如:data, state, amount, value, manager, engine, object, entity和instance。
一個好的命名能夠在閱讀者的腦海中描畫出一幅圖畫。而將某變數命名為”manager”並不能向讀者傳達任何有關該變數是做什麼的資訊。它是用來做績效評估的嗎? 它是管理加薪的嗎?
在命名時可以問一下自己,把這個單詞去掉含義是不是不變?如果是,那就果斷把它剔除吧~~
實際例子——華夫餅
為了讓大家瞭解以上的命名原則在實際中如何應用,這有個違法了以上所有原則的反例。這個例子和我實際上review過的一段程式碼一樣令人心碎。。。。
// 好吃的比利時華夫餅 class DeliciousBelgianWaffleObject { void garnishDeliciousBelgianWaffleWithStrawberryList( List<Strawberry> strawberryList) { ... } }
首先,通過引數列表我們可以知道方法是用來處理一個strawberry的列表,所以可以在方法命名中去掉:
class DeliciousBelgianWaffleObject { void garnishDeliciousBelgianWaffle( List<Strawberry> strawberries) { ... } }
除非程式中還包含不好吃的比利時華夫餅或者其他國家的華夫餅,不然我們可以將這些無用的形容詞去掉:
class WaffleObject { void garnishWaffle(List<Strawberry> strawberries) { ... } }
方法是包含在WaffleObject
類中的,所以方法名中無需Waffle的說明:
class WaffleObject { void garnish(List<Strawberry> strawberries) { ... } }
很明顯它是一個物件,任何事物都是一個物件,這也就是傳說中的“面向物件”的含義,所以命名中無需帶有Object
:
class Waffle { void garnish(List<Strawberry> strawberries) { ... } }
好了,這樣看起來好多了。
以上就是我總結的相當簡潔的命名原則。可能有些人會覺得無需在命名上耗費太多的精力,但我認為命名是開發過程中最基本的任務之一。
————————————————–我是萌萌噠分界線————————————————
感覺變數或者方法的命名,看似簡單,實際很難,特別是想一個簡潔明瞭、可讀性高的命名。自己也經常用什麼data
, xxxlist
來命名,作者說的挺對的,前者沒什麼意義,後者又有點囉嗦。不過對於集合型別的變數,統一用名詞複數命名容易混淆。舉個例子對於Apple這個類來說,可能存在List和Map兩種集合型別的變數。個人覺得對List型別的變數可以採用名詞複數來命名,Map型別的變數可以採用valueByKey格式來命名,比較容易區分。