【《代碼整潔之道》精讀與演繹】之二 高質(zhì)量代碼的命名法則
本文與大家聊一聊編程中非常關(guān)鍵的一個(gè)點(diǎn),如何更好的對代碼命名。
本文引用地址:http://dyxdggzs.com/article/201608/296112.htm一、引言
《代碼整潔之道》這本書(shū)提出了一個(gè)觀(guān)點(diǎn):代碼質(zhì)量與其整潔度成正比,干凈的代碼,既在質(zhì)量上可靠,也為后期維護、升級奠定了良好基礎。書(shū)中介紹的規則均來(lái)自作者多年的實(shí)踐經(jīng)驗,涵蓋從命名到重構的多個(gè)編程方面,雖為一“家”之言,然誠有可資借鑒的價(jià)值。
但我們知道,很多時(shí)候,理想很豐滿(mǎn),現實(shí)很骨感,也知道人在江湖,身不由己。因為項目的緊迫性,需求的多樣性,我們無(wú)法時(shí)時(shí)刻刻都寫(xiě)出整潔的代碼,保持自己輸出的都是高質(zhì)量、優(yōu)雅的代碼。
但若我們理解了代碼整潔之道的精髓,我們會(huì )知道怎樣讓自己的代碼更加優(yōu)雅、整潔、易讀、易擴展,知道真正整潔的代碼應該是怎么樣的,也許就會(huì )漸漸養成持續輸出整潔代碼的習慣。
而且或許你會(huì )發(fā)現,若你一直保持輸出整潔代碼的習慣,長(cháng)期來(lái)看,會(huì )讓你的整體效率和代碼質(zhì)量大大提升。
二、本文涉及知識點(diǎn)思維導圖
先放出這篇文章所涉及內容知識點(diǎn)的一張思維導圖,就開(kāi)始正文。大家若是疲于閱讀文章正文,直接看這張圖,也是可以Get到本文的主要知識點(diǎn)的大概。
三、高質(zhì)量代碼的命名法則
1 名副其實(shí)
名副其實(shí)說(shuō)起來(lái)貌似很簡(jiǎn)單,但真正做起來(lái),似乎沒(méi)那么容易。選個(gè)好名字要花一些時(shí)間,但其實(shí)選好名字之后省下來(lái)的時(shí)間,要比之前選名字時(shí)花掉的時(shí)間多得多。
我們一旦發(fā)現有更好的名稱(chēng)時(shí),就應該換掉之前的舊名稱(chēng),這樣做讀你代碼的人(包括你自己),都會(huì )很開(kāi)心。
一個(gè)好的變量、函數或類(lèi)的名稱(chēng)應該已經(jīng)幾乎答復了所有的大問(wèn)題。它應該告訴你,這個(gè)名稱(chēng)所代表的內容,為什么會(huì )存在,做了什么事情,應該如何用等。
如果一個(gè)名稱(chēng)需要注釋來(lái)補充才能讓大家明白其真正含義,那其實(shí)就不算是名副其實(shí)。(并不是說(shuō)不需要注釋?zhuān)∪缙浞值淖⑨屖浅绦騿T讓自己代碼錦上添花的好方法,關(guān)于注釋的一些注意事項,稍后會(huì )有文章專(zhuān)門(mén)涉及。)
舉個(gè)栗子:
以下的這句代碼里的d就不算是個(gè)好命名。名稱(chēng)d什么都沒(méi)說(shuō),它沒(méi)引起我們對時(shí)間消逝的感覺(jué),更別說(shuō)單位是天了:
[cpp] view plain copy print?
int d; // elapsed time in days||經(jīng)過(guò)了幾天時(shí)間
我們應該選擇這樣的指明了計量對象和計量單位的名稱(chēng):
[cpp] view plain copy print?
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
2 避免造成誤導
我們應該避免留下隱藏代碼本意的錯誤線(xiàn)索,也應該避免使用與本意相悖的詞。例如,別用accountList來(lái)指一組賬號,除非它真的是List類(lèi)型,用accountGroup、bunchOfAccounts,或者直接用accounts,都是更好的選擇。
盡量提防長(cháng)得太像的名稱(chēng)。想區分XYZControllerForEfficientHandlingOfStrings和XYZControllerForEfficientStorageOfStrings,會(huì )花費我們太多的時(shí)間。因為這兩個(gè)詞,實(shí)在太相似了。
以同樣的方式拼寫(xiě)出同樣的概念才是信息,拼寫(xiě)前后不一致就是誤導。
3 盡量做有意義的區分
1.盡量避免使用數字系列命名(a1、a2…….aN)。這樣的名稱(chēng)純屬誤導,因為很多時(shí)候完全沒(méi)有提供正確的信息,沒(méi)有提供導向作者意圖的線(xiàn)索。
2.廢話(huà)是另一種沒(méi)有意義的區分。如果我們有一個(gè)Product類(lèi),還有一個(gè)ProductInfo或ProductData類(lèi),那么他們的名稱(chēng)雖然不同,但意思卻無(wú)區別。這里的Info、Data就像a、an和the一樣,是意義含混的廢話(huà)。
注意,只要體現出有意義的區分,使用a、the這樣的前綴就沒(méi)錯。例如,將a用在域內變量,把the用于函數參數。
4 盡量使用讀得出來(lái)的名稱(chēng)
我們要使用讀得出來(lái)的名稱(chēng)。如果名稱(chēng)讀不出來(lái),討論的時(shí)候就會(huì )不方便且很尷尬,甚至讓旁人覺(jué)得很蠢。
例如,變量名稱(chēng)是beeceearrthreecee,討論的時(shí)候讀起來(lái)簡(jiǎn)直像沒(méi)吃藥。
5 盡量使用可搜索的名稱(chēng)
單字母和數字常量有個(gè)問(wèn)題,就是很難再一大篇文字中找出來(lái)。
找MAX_CLASSED_PER_STUDENT很容易,但想找數字7,就很麻煩。
同樣,字母e也不是個(gè)便于搜索的好變量名。因為作為英文中最常用的字母,在每個(gè)程序、每段代碼中都有可能出現。
名稱(chēng)長(cháng)短應與其作用域大小相對應,若變量或常量可能在代碼中多處使用,應賦予其以便于搜索的名稱(chēng)。
舉個(gè)栗子,比較如下兩段代碼:
[cpp] view plain copy print?
for (int j=0; j<34; j++)
{
s += (t[j]*4)/5;
}
和
[cpp] view plain copy print?
const int WORK_DAYS_PER_WEEK = 5;
int sum = 0;
for (int j=0; j < NUMBER_OF_TASKS; j++)
{
int realTaskDays = taskEstimate[j] * realDaysPerIdealDay;
int realTaskWeeks = (realdays / WORK_DAYS_PER_WEEK);
sum += realTaskWeeks;
}
按整潔代碼的要求來(lái)評判,第一段代碼會(huì )讓讀者不知所云,第二段代碼比第一段好太多。第二段代碼中,sum并非特別有用的名稱(chēng),但至少他搜索得到。采用能表達意圖的名稱(chēng),貌似拉長(cháng)了函數代碼,但要想想看,WORK_DAYS_PER_WEEK要比數字5好找得多,而列表中也只剩下了體現我們意圖的名稱(chēng)。
6 取名不要繞彎子
我們取名的時(shí)候要避免思維映射,不應當讓讀者在腦中把你的名稱(chēng)翻譯為他們熟知的名稱(chēng),也就是說(shuō)取名不要繞彎子,而是要直白,直截了當。
在多數情況下,單字母不是個(gè)好的命名選擇,除非是在作用域小、沒(méi)有名稱(chēng)沖突的地方,比如循環(huán)。循環(huán)計數器自然有可能被命名為i,j或k(最好別用字母l),這是因為傳統上我們慣用單字母名稱(chēng)做循環(huán)計數器。
程序員通常都是聰明人,聰明人有時(shí)會(huì )借助腦筋急轉彎炫耀其聰明。而聰明程序員和專(zhuān)業(yè)程序員之間的區別在于,專(zhuān)業(yè)程序員了解,明確就是王道。專(zhuān)業(yè)的程序員善用其能,能編寫(xiě)出其他人能理解的代碼。
7 類(lèi)名盡量用名詞
類(lèi)名盡量用名詞或名詞短語(yǔ),比如Customer, WikiPage,Account, 或 AddressParser。
類(lèi)名最好不要是動(dòng)詞。
8 方法名盡量用動(dòng)詞
方法名盡量用動(dòng)詞或動(dòng)詞短語(yǔ)。比如postPayment, deletePage, 或者save。
屬性訪(fǎng)問(wèn)器、修改器和斷言應該根據其value來(lái)命名,并根據標準加上get、set和is前綴。
舉個(gè)栗子,這里的getName、setName等命名都很OK:
[cpp] view plain copy print?
string name = employee.getName();
customer.setName("mike");
if (paycheck.isPosted())...
而重載構造器時(shí),使用描述了參數的靜態(tài)工廠(chǎng)方法名。如:
[cpp] view plain copy print?
Complex fulcrumPoint =Complex.FromRealNumber(666.0);
通常好于:
[cpp] view plain copy print?
Complex fulcrumPoint = new Complex(666.0);
我們也可以考慮將相應的構造器設置為private,強制使用這種命名手段。
9 每個(gè)概念對應一詞,并一以貫之
我們需給每個(gè)概念選一個(gè)詞,并且一以貫之。
例如,使用fetch、retrieve和get來(lái)給在多個(gè)類(lèi)中的同種方法命名,你怎么記得住哪個(gè)類(lèi)中是哪個(gè)方法呢?
同樣,在同一堆代碼中混用controller、manager,driver,就會(huì )令人困惑。DeviceManager和Protocol-Controller之間有何根本區別?為什么不全用controller或者manager?他們都是Driver嗎?這就會(huì )讓讀者以為這兩個(gè)對象是不同的類(lèi)型,也分屬不同的類(lèi)。
所以,對于那些會(huì )用到你代碼的程序員,一以貫之的命名法簡(jiǎn)直就是天降福音。
10 通俗易懂
我們應盡力寫(xiě)出易于理解的變量名,把代碼寫(xiě)得讓別人能一目了然,而不必讓人去非常費力地去揣摩其含義。我們想要那種大眾化的作者盡責地寫(xiě)清楚的通俗易懂的暢銷(xiāo)書(shū)風(fēng)格,而不是那種學(xué)者學(xué)院風(fēng)的晦澀論文寫(xiě)作風(fēng)格。
11 盡情使用解決方案領(lǐng)域專(zhuān)業(yè)術(shù)語(yǔ)
記住,只有程序員才會(huì )讀你寫(xiě)的代碼。所以,盡管去用那些計算機科學(xué)(Computer Science,CS)領(lǐng)域的專(zhuān)業(yè)術(shù)語(yǔ)、算法名、模式名、數學(xué)術(shù)語(yǔ)。
對于熟悉訪(fǎng)問(wèn)者(Visitor)模式的程序來(lái)說(shuō),名稱(chēng)AccountVisitor富有意義。給技術(shù)性的事物取個(gè)恰如其分的技術(shù)性名稱(chēng),通常就是最靠譜的做法。
12 添加有意義的語(yǔ)境
很少有名稱(chēng)是可以自我說(shuō)明的。所以,我們需要用有良好命名的類(lèi),函數或名稱(chēng)空間來(lái)放置名稱(chēng),給讀者提供語(yǔ)境。若沒(méi)能提供放置的地方,還可以給名稱(chēng)添加前綴。
舉個(gè)栗子,假如我們有名為firstName、lastName、street、houseNumber、city、state和zipcode的變量。當他們擱一塊兒的時(shí)候,的確是構成了一個(gè)地址。不過(guò),假如只是在某個(gè)方法中看到一個(gè)孤零零的state呢?我們會(huì )推斷這個(gè)變量是地址的一部分嗎?
我們可以添加前綴addrFirstName、addrLastName、addrState等,以此提供語(yǔ)境。至少,讀者可以知道這些變量是某個(gè)更大變量的一部分。當然,更好的方案是創(chuàng )建名為Address的類(lèi)。這樣,即便是編譯器也會(huì )知道這些變量是隸屬于某個(gè)更大的概念了。
另外,只要短名稱(chēng)足夠好,對含義的表達足夠清除,就要比長(cháng)名稱(chēng)更合適。添加有意義的語(yǔ)境甚好,別給名稱(chēng)添加不必要的語(yǔ)境。
四、小結
其實(shí),取一個(gè)好名字最難的地方在于需要良好的描述技巧和共有的文化背景。與其說(shuō)這是一種技術(shù)、商業(yè)或管理問(wèn)題,還不如說(shuō)這是一種教學(xué)問(wèn)題。
不妨試試上面列出的這十二條規則與要點(diǎn),看看你的代碼可讀性是否有所提升。而如果你是在維護別人的代碼,或者是在重構,效果應該會(huì )是立竿見(jiàn)影的。
五、本文涉及知識點(diǎn)提煉整理
文章開(kāi)頭部分已經(jīng)用思維導圖的方式展現了本文的知識點(diǎn),這邊再貼出一個(gè)文字列表版,方便大家整理:
要點(diǎn)一:要名副其實(shí)。一個(gè)好的變量、函數或類(lèi)的名稱(chēng)應該已經(jīng)答復了所有的大問(wèn)題。一個(gè)好名稱(chēng)可以大概告訴你這個(gè)名稱(chēng)所代表的內容,為什么會(huì )存在,做了什么事情,應該如何用等。
要點(diǎn)二:要避免誤導。我們應該避免留下隱藏代碼本意的錯誤線(xiàn)索,也應該避免使用與本意相悖的詞。
要點(diǎn)三:盡量做有意義的區分。盡量避免使用數字系列命名(a1、a2…….aN)和沒(méi)有意義的區分。
要點(diǎn)四:盡量使用讀得出來(lái)的名稱(chēng)。如名稱(chēng)讀不出來(lái),討論的時(shí)候會(huì )不方便且很尷尬。
要點(diǎn)五:盡量使用可搜索的名稱(chēng)。名稱(chēng)長(cháng)短應與其作用域大小相對應,若變量或常量可能在代碼中多處使用,應賦予其以便于搜索的名稱(chēng)。
要點(diǎn)六:取名不要繞彎子。取名要直白,要直截了當,明確就是王道。
要點(diǎn)七:類(lèi)名盡量用名詞。類(lèi)名盡量用名詞或名詞短語(yǔ),最好不要是動(dòng)詞。
要點(diǎn)八:方法名盡量用動(dòng)詞。方法名盡量用動(dòng)詞或動(dòng)詞短語(yǔ)。
要點(diǎn)九:每個(gè)概念對應一詞,并一以貫之。對于那些會(huì )用到你代碼的程序員,一以貫之的命名法簡(jiǎn)直就是天降福音。
要點(diǎn)十:通俗易懂。應盡力寫(xiě)出易于理解的變量名,要把代碼寫(xiě)得讓別人能一目了然,而不必讓人去非常費力地去揣摩其含義。
要點(diǎn)十一:盡情使用解決方案領(lǐng)域專(zhuān)業(yè)術(shù)語(yǔ)。盡管去用那些計算機科學(xué)領(lǐng)域的專(zhuān)業(yè)術(shù)語(yǔ)、算法名、模式名、數學(xué)術(shù)語(yǔ)。
要點(diǎn)十二:要添加有意義的語(yǔ)境。需要用有良好命名的類(lèi),函數或名稱(chēng)空間來(lái)放置名稱(chēng),給讀者提供語(yǔ)境。若沒(méi)能提供放置的地方,還可以給名稱(chēng)添加前綴。
評論