代碼里的命名規(guī)則:錯(cuò)誤的和正確的對(duì)比

2013-06-07 09:39:40來(lái)源:aqee作者:

編程初學(xué)者總是把大量的時(shí)間用在學(xué)習(xí)編程語(yǔ)言,語(yǔ)法,技巧和編程工具的使用上。他們認(rèn)為,如果掌握了這些技術(shù)技巧,他們就能成為不錯(cuò)的程序員。然而,計(jì)算機(jī)編程的目的并不是關(guān)于精通這些技術(shù)、工具的,它是關(guān)于針對(duì)

編程初學(xué)者總是把大量的時(shí)間用在學(xué)習(xí)編程語(yǔ)言,語(yǔ)法,技巧和編程工具的使用上。他們認(rèn)為,如果掌握了這些技術(shù)技巧,他們就能成為不錯(cuò)的程序員。然而,計(jì)算機(jī)編程的目的并不是關(guān)于精通這些技術(shù)、工具的,它是關(guān)于針對(duì)特定領(lǐng)域里的特定問(wèn)題創(chuàng)造出相應(yīng)的解決方案,程序員通過(guò)相互合作來(lái)實(shí)現(xiàn)這些。所以,很重要的一點(diǎn),你需要能精確的用代碼表達(dá)出你的思想,讓其他人通過(guò)代碼能明白你的意圖。

讓我們先看看編程大師Robert C. Martin的杰作《Clean Code》里的一句話:

“注釋的目的是為了彌補(bǔ)代碼自身在表達(dá)上的不足。”

這句話可以簡(jiǎn)單的理解為如果你的代碼需要注釋,最有可能是你的代碼寫的很爛。同樣,如果在沒(méi)有注釋的情況下你無(wú)法用代碼完整的表達(dá)你對(duì)一個(gè)問(wèn)題或一個(gè)算法的思路,那這就是一個(gè)失敗的信號(hào)。最終,這意味著你需要用注釋來(lái)闡明一部分的思想,而這部分在代碼里是看不出來(lái)的。好的代碼能夠讓任何人在不需要任何注釋的情況下看懂。好的編碼風(fēng)格能將所有有助于理解這個(gè)問(wèn)題的所有信息都蘊(yùn)含在代碼里。

在編程理論中,有一個(gè)概念叫做“自我描述的源代碼”。對(duì)于一段代碼,一種常見(jiàn)的自我描述機(jī)制是遵循某種非嚴(yán)格定義的變量、方法、對(duì)象命名規(guī)則。這樣做的主要作用就是使源代碼更易讀易懂。所以,也就更容易維護(hù)和擴(kuò)展。

這篇文章里,我將舉出一些例子,說(shuō)明什么是“不好的代碼”,什么是“清楚的代碼”

命名要能揭示意圖

如何命名,在編程中這永遠(yuǎn)都是個(gè)老大難問(wèn)題。有些程序員喜歡簡(jiǎn)化、縮短或加密名稱,使得只有他們自己能懂。下面讓我們看一些例子:

不好的代碼:

int d;
// 天數(shù)
int ds;
int dsm;
int faid;

“d”可以表示任何東西。作者使用注釋來(lái)表明他的意圖,卻沒(méi)有選擇用代碼來(lái)表示。而“faid”很容易導(dǎo)致誤解為ID。

清楚的代碼:

int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;

命名時(shí)避免含義引起誤解的信息

錯(cuò)誤的信息比沒(méi)有信息更糟糕。有些程序員喜歡“隱藏”一些重要信息,有時(shí)候他們也會(huì)寫出一些讓人誤解的代碼。

不好的代碼:

Customer[] customerList;
Table theTable;

變量“customerList”其實(shí)不是個(gè)list。它是一個(gè)普通的array(或客戶集合)。除此之外,“theTable”是一個(gè)Table類型的對(duì)象(你可以用IDE容易的發(fā)現(xiàn)它的類型),“the”這個(gè)詞是個(gè)不必要的干擾。

清楚的代碼:

Customer[] customers;
Table customers;

命名要有合適的長(zhǎng)度

在高級(jí)編程語(yǔ)言中,變量名的長(zhǎng)度通常不太限制。變量名幾乎可以任何長(zhǎng)度。雖然如此,這也可能使代碼變得鬧心。

不好的代碼:

var theCustomersListWithAllCustomersIncludedWithoutFilter;
var list;

好的名稱應(yīng)該只含有必要的詞匯來(lái)表達(dá)一個(gè)概念。任何不必要的字詞都會(huì)使名稱變長(zhǎng)、難于理解。名稱越短越好,前提是能在上下文中表達(dá)完整的意思(下訂單這個(gè)場(chǎng)景中,“customersInOrder” 要比 “list” 好)。

清楚的代碼:

var allCustomers;
var customersInOrder;

命名時(shí)編碼規(guī)范保持一致,讓規(guī)范幫助理解代碼

所有的編程技術(shù)(語(yǔ)言)都有自己的“風(fēng)格”,叫做編碼規(guī)范。程序員應(yīng)該在寫代碼時(shí)遵循這些習(xí)慣,因?yàn)槠渌某绦騿T也知道這些,并按這種風(fēng)格編寫。下面我們看一個(gè)沒(méi)有明顯規(guī)范的不好的代碼例子。下面的這段代碼沒(méi)有遵循很好的已知的“編碼規(guī)范”(比如PascalCase, camelCase, Hungarian規(guī)范)。更糟糕的是,這有一個(gè)毫無(wú)意義的bool變量“change”。這是個(gè)動(dòng)詞(用來(lái)描述動(dòng)作),但這里的bool值是來(lái)描述一個(gè)狀態(tài),所以,這里應(yīng)該用一個(gè)形容詞更合適。

不好的代碼:

const int maxcount = 1
bool change = true
public interface Repository
private string NAME
public class personaddress
void getallorders()

一段代碼,只看它的一部分,你就應(yīng)該直接明白它是什么類型,只需要看它的命名方法。

例如:你看到了“_name”,你就能知道它是個(gè)私有變量。你應(yīng)該在任何地方都利用這種表示方法,沒(méi)有例外情況。

清楚的代碼:

const int MAXCOUNT = 1
bool isChanged = true
public interface IRepository
private string _name
public class PersonAddress
void GetAllOrders()

命名時(shí)相同的概念用相同的詞表達(dá)

定義概念很難。在軟件開(kāi)發(fā)過(guò)程中,很多時(shí)間都花在分析業(yè)務(wù)場(chǎng)景、思考正確的定義里面所有的元素。這些概念永遠(yuǎn)都是讓程序員頭痛的事。

不好的代碼:

//1.
void LoadSingleData()
void FetchDataFiltered()
Void GetAllData()
//2.
void SetDataToView();
void SetObjectValue(int value)

首先:

代碼的作者試圖表達(dá)“get the data”的概念,他使用了多個(gè)詞“load”,“getch”,“get”。一個(gè)概念只用一個(gè)詞表達(dá)就行了(在同一個(gè)場(chǎng)景中)。

第二:

“set”這個(gè)詞用在了2個(gè)概念里:第一是“data loading to view”,第二個(gè)是“setting a value of object”。這是兩個(gè)不同的概念,你應(yīng)該使用不同的詞。

清楚的代碼:

//1.
void GetSingleData()
void GetDataFiltered()
Void GetAllData()
//2.
void LoadDataToView();
void SetObjectValue(int value)

命名時(shí)使用跟業(yè)務(wù)領(lǐng)域相關(guān)的詞

程序員寫的所有代碼都是跟業(yè)務(wù)領(lǐng)域場(chǎng)景邏輯相連的。為了讓所有關(guān)系到這個(gè)問(wèn)題的人都能更好的理解,程序中應(yīng)該使用在領(lǐng)域環(huán)境中有意義的名稱。

不好的代碼:

public class EntitiesRelation
{
Entity o1;
Entity o2;
}

當(dāng)在編寫針對(duì)某個(gè)領(lǐng)域的代碼時(shí),你應(yīng)該始終考慮使用領(lǐng)域有聯(lián)系的名稱。在將來(lái),當(dāng)另外一個(gè)人(不僅是程序員,也許是測(cè)試人員)接觸你的代碼時(shí),他能輕松的理解這個(gè)業(yè)務(wù)領(lǐng)域里你的代碼是什么意思(不需要業(yè)務(wù)邏輯知識(shí))。你首先考慮的應(yīng)該是業(yè)務(wù)問(wèn)題,之后才是如何解決。

清楚的代碼:

public class ProductWithCategory
{
Entity product;
Entity category;
}

命名時(shí)使用在特定環(huán)境里有意義的詞

代碼里名稱都有自己的上下文。上下文對(duì)于理解一個(gè)名稱非常重要,因?yàn)樗芴峁╊~外的信息。讓我們來(lái)看看一個(gè)典型的“地址”上下文:

不好的代碼:

string addressCity;
string addressHomeNumber;
string addressPostCode;

在大多數(shù)情況中,“Post Code”通常是地址的一部分,很顯然,郵政編碼不能單獨(dú)使用(除非你是在開(kāi)發(fā)一個(gè)專門處理郵編的應(yīng)用)。所以,沒(méi)有必要在“PostCode”的前面加上“address”。更重要的,所以的這些信息都有一個(gè)上下文容環(huán)境,一個(gè)命名空間,一個(gè)類。

在面向?qū)ο缶幊讨,這里應(yīng)該用一個(gè)“Address”類來(lái)表達(dá)這個(gè)地址信息。

清楚的代碼:

class Address
{
string city;
string homeNumber;
string postCode;
}

命名方法總結(jié)

概述起來(lái),做為一個(gè)程序員,你應(yīng)該:

  • 命名是來(lái)表達(dá)概念的
  • 注意名稱長(zhǎng)度,名稱里只該含有必要的詞語(yǔ)
  • 編碼規(guī)范有助于理解代碼,你應(yīng)該使用它
  • 名稱不要混用
  • 名稱在業(yè)務(wù)領(lǐng)域里要有意義,在上下文里有意義
英文原文:Express names in code: Bad vs Clean 
關(guān)鍵詞:代碼命名規(guī)則

贊助商鏈接: