程序員如何讓你的變量名更加精確
關(guān)鍵點(diǎn)
“別人還能把這個(gè)名字理解成什么意思?”通過不斷的問自己這個(gè)問題來積極檢查每一個(gè)命名。
事實(shí)上,這種富有創(chuàng)造性的、不斷嘗試“錯(cuò)誤理解”的方法,能夠有效的發(fā)現(xiàn)歧義的命名,并修正它們。正如本文中的示例,我們將隨時(shí)通過“騎驢看唱本 ——邊走邊瞧”的方式來 探討所見到名字的誤解之處,然后選取一個(gè)更好的名字。
示例:Filter()
假設(shè)寫了一段代碼來操作數(shù)據(jù)庫(kù)結(jié)果的集合:
results= Database.all_objects.filter("year <= 2011")
那么,results包含什么數(shù)據(jù)呢?
所有滿足year<=2011的對(duì)象
所有不滿足year<=2011的對(duì)象
問題的由來是從filter這個(gè)有歧義的詞開始的,它沒有清楚表達(dá)它的意思是“選取”還是“剔除”。因此,應(yīng)該避免使用filter,它太容易造 成誤解!
如果這里想要的效果是“選取”,一個(gè)更好的名字是select;如果想要的是“剔除”,更好的名字則是exclude。
為布爾值取名
當(dāng)為布爾值變量命名或者函數(shù)返回布爾值的時(shí)候,要特別注意真和假所表達(dá)出來的真實(shí)意思,這里就有一個(gè)很危險(xiǎn)的例子:
bool read_password= true;
這句代碼意思取決于當(dāng)時(shí)怎么閱讀的(沒有其他的意思了),顯然這里有兩種截然不同的理解:
需要讀密碼
密碼已經(jīng)被讀過了
在這個(gè)用例下,做好避免用單詞read,可以考慮使用need_password或者user_is_authenticated來代替。
通常情況下,添加單詞is、has、can或者should可以讓布爾值的意思更加清晰易懂。
比如說有個(gè)函數(shù)叫SpaceLeft(),乍一看,就會(huì)想到這個(gè)函數(shù)返回的值是數(shù)字。如果需要明確返回值是布爾值,一個(gè)更好的名字是 HasSpaceLeft()。
還有,盡量避免使用反義短句來命名。例如:
bool disable_ssl= false;
改成如下代碼則更容易理解,同時(shí)更契合原意:
bool use_ssl= true;
符合用戶期望
很多名字是帶有誤導(dǎo)性的,因?yàn)閷?duì)于某個(gè)名字,用戶自已有一個(gè)預(yù)想的定義,但是代碼的意思可能恰恰不是這個(gè)意思。如此情況下,***作出“讓步”并改 變名字,消除 誤導(dǎo)性。
示例:get*()
許多程序員都在使用這樣的編碼規(guī)范:某個(gè)方法以get開頭來表達(dá)一個(gè)“輕量級(jí)的訪問器”以返回內(nèi)部成員。違反這個(gè)規(guī)范將很容易誤導(dǎo)用戶。 避免下面的例子中java代碼段的做法:
public class StatisticsCollector { public void addSample(double x) { ... } public double getMean() { // Iterate through all samples and return total / num_samples } ...}
這里,getMean的實(shí)現(xiàn)是枚舉過去所有的數(shù)據(jù),并計(jì)算其平均值。如果數(shù)據(jù)量很大的時(shí)候,這一步的開銷將會(huì)是非常大的。但是,一個(gè)不了解情況的 程 序員則會(huì)很粗心的調(diào)用它并且假設(shè)這是一個(gè)很廉價(jià)的調(diào)用。
因此,這個(gè)方法應(yīng)該改名成類似computeMean()這樣的,看起來這樣就是一個(gè)代價(jià)高昂的操作了(或者,另一個(gè)選擇就是改寫其實(shí)現(xiàn),變成一 個(gè)名副其實(shí)的輕量級(jí)操作)。
示例:list::size()
這里講一個(gè)C++標(biāo)準(zhǔn)庫(kù)里的命名問題。這段代碼導(dǎo)致的結(jié)果是,很難定位和修復(fù)類似導(dǎo)致服務(wù)器龜速運(yùn)行之類的問題:
void ShrinkList(list<Node>& list, int max_size) { while (list.size()>max_size) { FreeNode(list.back()); list.pop_back(); }}
這樣的bug的導(dǎo)致是作者沒有意識(shí)到list.size()是一個(gè)O(n)復(fù)雜度的操作——它挨個(gè)計(jì)數(shù)鏈表的節(jié)點(diǎn)得出總數(shù)而不是返回已計(jì)算 好的總個(gè)數(shù),這將導(dǎo)致ShrintList是一個(gè)O(n2) 的操作。
從技術(shù)角度講,這段代碼沒有問題,也能通過所有的單元測(cè)試。但是當(dāng)調(diào)用ShrintList()并傳入一個(gè)包含上億數(shù)量級(jí)的list時(shí),它可能將 耗費(fèi)數(shù)小時(shí)的時(shí)間。
或許你會(huì)認(rèn)為,這個(gè)是調(diào)用者的錯(cuò)誤使用,他/她沒有認(rèn)真仔細(xì)的閱讀相關(guān)的文檔!確實(shí)是這樣的,但是,事實(shí)上,這里的list.size()不是一 個(gè)恒準(zhǔn)時(shí)(constant-time)操作,這太意外了!其他所有的C++容器類都是恒準(zhǔn)時(shí)的size()方法呀。
假如把size()更名成countSize()或者countElements(),類似的錯(cuò)誤就會(huì)大大減少了。C++標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)者可能想的 是使用一個(gè)size()方法去和其他的容器匹配,像vector和map,這樣API的一致性看起來更好。正是由于這樣的做了,導(dǎo)致程序員容易誤 用并認(rèn)為這是一個(gè)很快的操作,和其他的容器一樣!幸運(yùn)的是,***的C++標(biāo)準(zhǔn)要求size()是O(1)復(fù)雜度。
原文鏈接:http://www.cnblogs.com/powertoolsteam/archive/2011/11/16/2250700.html