[代碼大全讀書筆記]如何定義一個好的變量名

在平常編程中最煩惱的就是給變量取名,一段好代碼,既要能完美地運行,還要能較容易地維護。這就意味着須要讓往後維護代碼的人能很快地看懂你的代碼,並且,在團隊合做中,其餘開發者也會常常閱讀你那部分代碼。若是你的代碼中充滿了a,b,c,a1,a2,a3...那簡直就是一個噩夢。所以,好的變量名很是重要。程序員

選擇好變量名的注意事項

變量和變量名本質上是同一件事物,所以,變量的好與壞就在很大程度上取決於它的命名的好與壞。編程

下面舉一個糟糕命名的例子數組

$pp = ($cp > 1) ? ($cp - 1) : $cp;
$np = ($cp < $tp) ? ($cp + 1) : $tp;

$p = new P($pp, $cp, $np, $tp);

這段代碼在作什麼呢?也許能夠大概知道是在計算一些信息,可是,計算的是什麼信息呢?$p,$pp,np,$tp等等這些變量表明的是什麼呢?講真,若是沒有任何註釋,任何人都沒法看不懂這段代碼想表達的意思。若是寫這段代碼的人告訴你,這段代碼是在計算分頁信息,而後實例化一個分頁類,那麼你應該如何命名呢?app

下面是這段代碼的另外一種寫法,看起來更加清晰:

$prev_page_num = ($curr_page_num > 1) ? ($curr_page_num - 1) : $curr_page_num);
$next_page_num = ($next_page_num < $total_page_num) ? ($curr_page_num + 1) : $total_page_num);

$page = new Page($prev_page_num, $curr_page_num, $next_page_num, $total_page_num);

從上面兩段代碼能夠看出,一個好的變量名在可讀性和可維護性上是極其重要的。並且好的變量名是易記的。能夠經過應用多條原則來實現這些目標。編程語言

最重要的命名注意事項

  • 名字要徹底、準確地描述出該變量所表明的事物學習

  • 用名字表達變量所表明的是什麼,不包含晦澀的縮寫,同時也沒有歧義測試

下表給出一些變量名稱的例子,其中有好的也有差的。編碼

變量用途 好名字,好描述 壞名字,差描述
到期的支票累計額 runningTotal,checkTotal written,ct,checks,CHKTTL,x,x1,x2
高速列車的運行速度 velocity,trainVelocity,velocityInMph velt,v,tv,x,x1,x2,train
當前日期 currentDate,todaysDate cd,current,c,x,x1,x2,date
每頁的行數 linesPerPage lpp,lines,l,x,x1,x2

currentDate和todaysDate都是很好的名字,由於它們都徹底並且準確地描述出了「當前日期」這一律念。指針

cd和c是很糟糕的命名,由於它們用了過短的縮寫,並且又不具備描述性。調試

current也很糟,由於它並無告訴你是當前什麼。

date看上去不錯,但通過最後推敲它也只是個壞名字,由於這裏所說的日期並非全部的日期都可,而只是特指當前日期,而date自己並未表達出這層含義。

x,x1和x2永遠都是壞名字--傳統上用x表明一個未知量,若是不但願你的變量所表明的是一個未知量,那麼請考慮取一個更好的名字吧。

名字應該儘量地明確。像x、temp、i這些名字都泛泛可得能夠用於多種目的,它們並無像應該的那樣提供足夠信息,所以一般是命名上的敗筆。

以問題爲導向

一個好記的名字反映的一般都是問題,而不是解決方案。即,一個好名字一般表達的是什麼(What),而不是怎麼樣(How)。一般來講,若是一個名字反映了計算機的某些方面而不是問題自己,那麼它反映的就是「How」而非「What」了。

好比,考慮下面這兩個變量命名:inputRec和employeeData。inputRec是一個反映輸入、記錄這些計算概念的計算機術語。employeeData則直指問題領域,與計算機無關。
相似地,printerReady比bitFlag更能表達打印機的狀態;在財務軟件裏,calcVar比sum來得更準確。

最適當的名字長度

經研究發現,變量名的平均長度在10到16個字符的時候,調試花的力氣是最小的。平均名字長度在8到20隔字符的程序也幾乎一樣容易調試。這並不意味着你的變量名必定要在8到20個字符,它強調的是,若是你查看本身寫的代碼時發現了不少更短的名字,那麼你就須要認真檢查,確保這些名字含義足夠清晰。

下面展現變量名太長、過短或恰好的示例:

太長 : numberOfPeopleOnTheUsOlympicTeam; numberOfSeatsInTheStadium; maximumNumberOfPointsInModernOlympics

過短 : n, np, ntm; n, ms, nsisd; m, mp, max, points

正好 : numTeamMembers, teamMemberCount; numSeatsInStadium, seatCount; teamPointsMax, pointsRecord

變量名中的計算值限定詞

不少程序都有表示計算結果的變量:總額、平均值、最大值,等等。若是你要用相似Total、Sum、Average、Max、Min、Record、String、Pointer這樣的限定詞來修改某個名字,那麼請記住把限定詞加到名字的最後。

這種方法的優勢:

  • 變量名中最重要的那部分,即爲這一變量賦予主要含義的部分應當位於最前面,這樣,這一部分就能夠顯得最爲突出,並會被首先閱讀到;

  • 避免了因爲同時在程序中使用totalRevenue和revenueTotal而產生的歧義

  • 使用統一的編碼規範能夠提升可讀性,簡化維護工做。好比,revenueTotal、expenseTotal、revenueAverage、expenseAverage這組名字具備很是優雅的對稱性。而totalRevenue、expenseTotal、revenueAverage、averageRevenue這組名字中則看不出什麼規律來。

這條規則也有例外,那就是Num的限定詞的位置已是約定俗成的。Num放在變量名的開始位置表明一個總數,好比:numCustomers表示員工的總數。Num放在變量名的結束位置表明一個下標:customerNum表示的是當前員工的序號。這樣使用Num經常會帶來麻煩,所以,最好的方法是避開這些問題,使用Count或者Total來表明總數,使用Index來指代某個特定的員工。這樣,customerCount就表明員工的總數,customerIndex表明某個特定的員工。

變量名中的經常使用對仗詞

對仗詞要使用正確,否則會產生歧義。

經常使用對仗詞以下:

  • begin/end

  • first/last

  • locked/unlocked

  • min/max

  • next/previous

  • old/new

  • opened/closed

  • visible/invisible

  • source/target

  • source/destination

  • up/down

爲特定類型的數據命名

爲變量命名,除了一般的考慮事項以外,爲一些特定類型數據的命名還要求做出一些特殊的考慮。好比,循環變量、狀態變量、臨時變量等等。

爲循環下標命名

在循環中,最多見的下標變量就是i,j,k,如:

for(i = 0; i < arrLen; i++) {
    // ...
}

若是循環下標變量只在循環內部使用,那麼如此使用是沒問題的,可是,若是該變量須要在循環以外使用,那麼就應該爲它取一個比i,j,k更有意義的名字。舉個栗子,若是你從文件中讀取記錄,而且須要記下所讀取記錄的數量,那麼相似於redcordCount這樣的名字就更合適:

recordCount = 0;
while ( moreScores() ) {
    score[recordCount] = GetNextScore();
    recordCount++;
}

// using recordCount

另外一種狀況就是嵌套循環,比較常犯的錯誤就是在想寫j的時候寫了i,想用i的時候卻寫了j。

若是你使用了多個嵌套的循環,那麼就應該給循環變量賦予更長的名字以提升可讀性:

for ( teamIndex = 0; teamIndex < teamCount; teamIndex++) {
    for ( eventIndex = 0; eventIndex < eventCount[teamIndex]; eventIndex++) {
        score[teamIndex][eventIndex] = 0;
    }
}

scoreteamIndex 比 scorei給出的信息更多。

注意:若是你必定要用i、j、k,那麼不要把它們用於簡單循環的循環下標以外的任何場合,避免形成誤解。要想避免這種問題,最簡單的方法就是使用更好的命名而不是i,j,k。

爲狀態變量命名

爲狀態變量取一個比flag更好的名字。

最好是把標記看做是狀態變量。標記的名字中不該該含有flag,由於你從中絲毫看不出該標記是作什麼的。

爲清楚可見,標記應該使用枚舉變量、具名常量,或用做具名常量的全局變量來對其賦值。

看看下面比較差的標記命名:

if ( flag ) ...
if ( statusFlag & 0x0F ) ...
if ( printFlag == 16 ) ...
if ( computeFlag == 0 ) ...

flag = 0x1;
statusFlag = 0x80;
printFlag = 16;
computeFlag = 0;

上面這段代碼反映不出能作什麼,若是沒有文檔,不知道statusFlag = 0x80的含義是什麼。下面是做用相同但更爲清晰的代碼:

if ( dataReady ) ...
if ( characterType & PRINTABLE_CHAR ) ...
if ( reportType == ReportTyoe_Annual ) ...
if ( recalcNeeded == false ) ...

dataReady = true;
characterType = CONTRAL_CHARACTER;
reportType = ReportType_Annual;
recalNeeded = false;

這段代碼更加清晰。並且說明你能夠結合枚舉類型和預約義的具名常量來使用這種方法。

若是你發現本身須要猜想某段代碼的含義的時候,就該考慮爲變量從新命名。代碼應該儘量直接讀懂。

爲臨時變量命名

臨時變量經常使用於存儲計算的中間結果,做爲臨時佔位符,以及存儲內部值。它們常被賦予temp,tmp,x或者其餘一些模糊且缺少描述性的名字。一般,臨時變量是一個信號,代表程序緣尚未徹底把問題弄清楚。並且,因爲這些變量被正式地賦予了一種「臨時」狀態,所以程序員會傾向於比其餘變量更爲隨意地對待這些變量,從而增長了出錯的可能。

警戒臨時變量

臨時地保存一些變量是頗有必要的。但不管從哪一種角度看,程序中的大多數變量都是臨時性的。把其中幾個稱爲臨時的,可能代表你尚未弄清它們的實際用途。看看下面的示例:

temp = sqrt( b^2 - 4*a*c );
root[0] = ( -b + temp ) / ( 2*a );
root[1] = ( -b - temp ) / ( 2*a );

更好的作法:

discriminant = sqrt( b^2 - 4*a*c );
root[0] = ( -b + discriminant ) / ( 2*a );
root[1] = ( -b - discriminant ) / ( 2*a );

discriminant,判別式

爲布爾變量命名

典型的布爾變量名:

  • done

  • error

  • found

  • success/ok

給布爾變量賦予隱含「真/假」含義的名字。像done和success同樣,它們的值不是true就是false,表示某件事情完成了或者沒有完成;成功或者失敗。另外一方面,想status這樣的名字倒是很糟的布爾變量名,由於它們沒有明確的true或者false。status是true反映的是什麼含義呢?表示某件事情擁有一個狀態嗎?然而,每件事情都有狀態。true代表某件事情的狀態是OK嗎?或者說false代表沒有任何錯誤嗎?對於status,你什麼都說不出。

爲了更好的效果,能夠把status命名爲error或者statusOK。

有時,也能夠在布爾變量名前加上Is。這樣,變量名就成了一個問題:isDone?isError?isFound?用true或false回答問題也就爲該變量給出了取值。優勢是不能用於那些模糊不清的名字,好比:isStatus?毫無心義。缺點就是下降了簡單邏輯表達式的可讀性:if(isFound)的可讀性要略差於if(Found)。

使用確定的布爾變量名。避免雙重否認:not notFound。

爲枚舉類型命名

在使用枚舉類型的時候,能夠經過使用組前綴,如Color_,Planet_或者Month_來明確標識該類型的成員都同屬於一個組。好比:

Public Enum Color
    Color_Red
    Color_Green
    Color_Blue
End Enum

Public Enum Planet
    Planet_Earth
    Planet_Mars
    Planet_Venus
End Enum

在有些編程語言裏,枚舉類型的處理很像類,枚舉類型也老是被冠以枚舉名字前綴,好比Color.Color_Red或者Planet.Planet_Earth。若是你正在使用這樣的編程語言,那麼重複上述前綴的意義就不大了,能夠簡化爲Color.Red和Planet.Earth。

爲常量命名

在具名常量時,應該根據該常量所表示的含義,而不是該常量所具備的數值爲該抽象事物命名。好比FIVE是個很糟糕的常量名,CYCLES_NEEDED是個不錯的名字。

命名規則的力量

不少程序員會抵制標準和約定(有時我也會這樣),而且有很好的理由:有些標準和約定很是刻板而且低效--它們會毀壞創造性和程序質量。

爲何要有規則

  • 要求你更多地按規矩行事。集中精力關注代碼更重要的特徵;

  • 有助於在項目之間傳遞知識;

  • 有助於在新項目中更快速地學習代碼;

  • 有助於減小名字增生,在沒有規則下,很容易給同一個對象起兩個不一樣的名字;

  • 彌補編程語言的不足之處;

  • 強調相關變量之間的關係。

關鍵是,採用任何一項規則都要好於沒有規則。規則多是武斷的。命名規則的威力並不是來源於你所採起的某個特定規則,而是來源於如下事實:規則的存在爲你的代碼增長告終構,減小了你須要考慮的事情。

什麼時候採用命名規則

  • 多個程序員合做開發一個項目時

  • 計劃把一個程序轉交給另外一位程序員來修改和維護的時候

  • 你所在組織中的其餘程序員評估你寫的程序的時候

  • 當你寫的程序規模過大,以至於你沒法在腦海裏同時瞭解事情的全貌,而必須分而治之的時候

  • 你寫的程序生命期足夠長,長到你可能會在把它擱置幾個星期或幾個月以後又從新啓動有關該程序的工做時

  • 當在一個項目中存在一些不常見的術語,而且你但願在編寫代碼階段使用標準的術語或縮寫的時候

非正式命名規則

儘管上面介紹了不少比較標準的命名規則,可是大多數項目採用的都是相對非正式的命名規則。

與語言無關的命名規則的指導原則

  • 區分變量名和子程序名字

  • 區分類和對象

  • 標識全局變量

  • 標識成員變量

  • 標識類型聲明

  • 標識具名常量

  • 標識枚舉類型的元素

  • 在不能保證輸入參數只讀的語言裏標識只讀參數

  • 格式化命名以提升可讀性

儘可能不要混用上述方法,那樣會使代碼更難閱讀。老老實實地堅持使用其中任意一種提升可讀性的方法,你的代碼質量必定會有所改善。

與語言相關的命名規則的指導原則

應該遵循你所用語言的命名規則。對於大多數語言,你均可以找到描述其風格原則的參考書,下面給出C的指導原則。

C的命名規則

  • c和ch是字符變量

  • i和j是整數下標

  • n表示某物的數量

  • p是指針

  • s是字符串

  • 預處理宏所有大寫,一般包括typedef

  • 變量名和子程序名所有小寫

  • 下劃線用做分隔符,如:letters_in_lowercase

標準前綴

對具備通用含義的前綴標準化,爲數據命名提供了一種簡潔、一致而且可讀性好的方法。

標準化的前綴由兩部分組成:用戶自定義類型(UDT)的縮寫和語義前綴。

用戶自定義類型縮寫

UDT縮寫能夠標識被命名對象或變量的數據類型。UDT縮寫一般不會表示任何由編程語言所提供的預置數據類型。下面列出一份UDT示例。

UDT縮寫 含義
ch 字符(Character)
doc 文檔(Document)
pa 段落(Paragraph)
scr 屏幕區域(Screen region)
sel 選中範圍(Selection)
wn 窗體(Window)

可使用上表列出的UDT類型定義下面這樣的數據聲明:

CH chCursorPosition;
SCR srcUserWorkSpace;
DOC docActive;
PA firstPaActiveDocument;
PA lastPaActiveDocument;
WN wnMain;

語義前綴

語義前綴比UDT更進一步,它描述了變量或者對象是如何使用的。並且語義前綴不會根據項目的不一樣而不一樣,對於不一樣的項目均是標準的。下面列出一組標準的語義前綴。

語義前綴 含義
c 數量(count)
first 數組中須要處理的第一個元素
g 全局變量
i 數組的下標
last 數組中須要處理的最後一個元素,與first對應
lim 數組中須要處理的元素的上限,一般,lim等於last+1
m 類一級的變量
max 數組或其餘種類的列表中絕對的最後一個元素
min 數組或其餘種類的列表中絕對的第一個元素
p 指針(pointer)

標準前綴的優劣

  • 能更爲精確地描述一些含義比較模糊的名字

  • 使名字變得更加緊湊

缺陷:程序員在使用前綴的同時忽略給變量其有意義的名字。

建立具有可讀性的短名字

若是環境真的要求你建立簡短的名字,請注意有些縮短名字的方法要好於其餘的方法。

縮寫的通常指導原則

下面列出幾項用於建立縮寫的指導原則。其中一些原則彼此衝突,因此不要試圖同時應用全部的原則。

  • 使用標準的縮寫

  • 去掉全部非前置元音(computer => cmptr, screen => scrn, apple => appl, interger => intgr)

  • 去掉虛詞and,or,the等

  • 使用每一個單詞的第一個或前幾個字母

  • 統一在每一個單詞的第1、第二或者第三個字母后截斷

  • 保留每一個單詞的第一個和最後一個字母

  • 使用名字中的每個重要單詞,最多不超過三個

  • 去掉無用的後綴--ing,ed等

  • 保留每一個音節中最引人注意的發音

  • 確保不要改變變量的含義

  • 反覆使用上述技術,直到把每一個變量名的長度縮減到了8-20個字符。

語音縮寫

有些人倡導基於單詞的發音而不是拼寫來建立縮寫,好比skating => sk8ing, before => b4...可是不提倡這麼作。

有關縮寫的評論

下面是一些可以用來避免犯錯的規則

  • 不要用從每一個單詞中刪除一個字符的方式來縮寫,要麼刪除不止一個字符,要麼就把單詞拼寫完整

  • 縮寫要一致,好比:要麼所有使用Num,要麼全用No,不要兩個都用

  • 建立你能讀出來到的名字,好比:用xPos而不用xPstn。能夠藉助電話來測試--若是你沒法在電話中向他人讀出你的代碼,就請從新給變量起一個更清晰的名字吧

  • 避免使用容易看錯或者讀錯的字符組合,好比ENDB和BEND,爲了表示B的結尾,能夠用一種好的分隔技術來命名:b_end/BEnd

  • 使用辭典來解決命名衝突,使用近義詞來解決命名衝突

  • 在代碼裏用縮寫對照表解釋極短的名字的含義

應該避免的名字

  • 避免使用使人誤解的名字或縮寫,好比將"Fig and Almond Season"縮寫爲FALSE

  • 避免使用具備類似含義的名字

  • 避免使用具備不一樣含義但卻有類似名字的變量

  • 避免使用發音相近的名字

  • 避免在名字中使用數字

  • 避免在名字中拼錯單詞

  • 避免使用英語中經常拼錯的單詞

  • 不要僅靠大小寫來區分變量名

  • 避免使用多種天然語言

  • 避免使用標準類型、變量和子程序的名字

  • 不要使用與變量含義徹底無關的名字

  • 避免在名字中包含易混淆的字符,好比1(數字1)和l(字母l),0(數字0)和O(字母O)

總結

好的變量名是提升程序可讀性的一項關鍵要素。代碼閱讀的次數遠遠多於編寫的次數,確保代碼中所取的名字更側重於閱讀方便而不是編寫方便。選擇一種規則,並堅持遵循該規則。

原創文章,文筆有限,才疏學淺,文中如有不正之處,萬望告知。

若是本文對你有幫助,請點下推薦吧,謝謝^_^

相關文章
相關標籤/搜索