Clean Code 無瑕的程式碼 第2章 有意義的命名
? ? ? ?第二章內(nèi)容是命名,命名很重要!通常工程師不太重視,大家都會想先把功能完成。
讓名稱代表意圖───使之名符其實
變數(shù)、函數(shù)與類別名稱應(yīng)該要可以得知為什麼會在這出現(xiàn)、做什麼用、如何使用。
如果一個名稱還需要註解輔助,表示名稱不具備表達能力
int d; // elapsed time in days
變數(shù) d 沒有傳達出任何資訊,應(yīng)該選擇能夠具體指名計量與計量單位的名稱。
int elapsedTimeInDays;
int daysSinceCreation;
int daysSinceModification;
int fileAgeInDays;
作者用三個範(fàn)例說明命名的重要性。
第一例
public List<int []> getThem()
{
????List<int[]>list1 = new ArrayList<int[]>();
????for(int[] x: theList)
????????if(x[0]===4)
????????????list1.add(x);
????return list1;
}
1.theList 裡存放著什麼類型的東西?
2.theList 裡索引0的項目,代表的意義是什麼?
3.數(shù)值4的意義是什麼?
4.我該如何使用回傳的列表?
第二例引用踩地雷遊戲,「正名」之後程式碼獲得改善。
public List<int []> getFlaggedCells()
{
????List<int[]> flaggedCells = new ArrayList<int[]>();
????for(int[] cell:gameBoard)
????????if(cell[STATUS_VALUE] == FLAGGED)
????????????flaggedCells.add(cell);
????return flaggedCells;
}
第三例將地雷格寫成Cell類別,並且擁有函數(shù)isFlag隱藏魔術(shù)數(shù)字(magic number)
public List<Cell> getFlaggedCells()
{
????List<Cell> flaggedCells = new ArrayList<Cell>();
????for(Cell cell:gameBoard)
????????if(cell.isFlaged())
????????????flaggedCells.add(cell);
????return flaggedCells;
}?
避免誤導(dǎo)
名稱如果只有一點點不同,使用上就要小心。
o與0容易誤判。
hp、aix與sco是平臺名稱,要避免使用。
產(chǎn)生有意義的區(qū)別
public static void copyChars(char a1[ ], char a2[ ])? 看不懂要做什麼。
public static void copyChars(char source[ ], char destination[ ]) 比較看得懂功能
getActiveAccount()
getActiveAccounts()
getActiveAccountInfo()
三個函數(shù)名稱太像容易混淆。
使用能唸出來的名稱
命名可唸出來可以增加可讀性。
例如 class DtaRcrd102 沒有可讀性 class Customer 比較有可讀性
使用可被搜尋的名字
搜尋MAX_CLASSES_PER_STUDENT 比較容易,搜尋 7 較難!會在很多地方找到7。
避免只使用一個英文字母或是簡短的英文字母加阿拉伯?dāng)?shù)字當(dāng)做變數(shù)。
作者偏好在小函數(shù)的區(qū)域變數(shù),才會使用單一字母的變數(shù)。命名長度應(yīng)該與其視野大小相對應(yīng)。
for (int j=0;j<34;j++)
{
????s+= (t[j]*4)/5
}
和
int realDaysPerIdealDay = 4;
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;
}
兩個範(fàn)例比較,sum可以被搜尋,尋找WORK_DAYS_PER_WEEK 比尋找5要容易多。
避免編碼
編碼如同學(xué)習(xí)新的語言,會給員工不必要的負擔(dān)。
匈牙利標誌法
古早時代編譯器不會進行資料型態(tài)的檢查,程式設(shè)計師必須使用匈牙利命名法記住資料型態(tài)。
現(xiàn)代編譯器會進行資料型態(tài)檢查,匈牙利命名法在變更資料型態(tài)的時候,反而會誤導(dǎo)程式設(shè)計師,作者反對使用匈牙利命名法。
成員的字首 (Member Prefixes)
古早成員變數(shù)會開頭會加上m_,作者認為編譯環(huán)境進步會協(xié)助程式設(shè)計師區(qū)分,可不用再加m_。
public class Part
{
????private String m_dsc; // The textual description
????void setName(String name)
????{
????????m_dsc = name;
????}
}?
public class Part
{
????String description;
????void setDescription(String description)
????{
????????this.description = description;
????}
}
介面(Interfaces)和實作(Implementations)
作者不喜歡在介面前加I或i表示介面。作者會在實作編碼例如ShapeFactoryImp。
避免思維的轉(zhuǎn)換
程式碼清楚明白才是正道。
類別的命名
類別名稱應(yīng)用名詞或名詞片語,避免使用動詞。
方法的命名
方法應(yīng)該用動詞或動詞片語命名。
javabean的標準,取出器(accessors)應(yīng)該使用get當(dāng)字首、修改器(mutator)應(yīng)該使用set當(dāng)字首、判定(predicates)應(yīng)該使用is當(dāng)字首。
設(shè)計模式工廠方法可以增加程式可讀性。
Complex fulcrumPoint = Complex.FromRealNumber(23);
可讀性勝過以下寫法
Complex fulcrumPoint = new Complex(23);
不要裝可愛
清楚比娛樂來得重要。
每個概念使用一種字詞
例如取得動作,出現(xiàn)fetch、retrieve和get 三種功能類似的函數(shù)會讓人混淆,所以作者建議每個概念使用一種字詞。
別說雙關(guān)語
避免出現(xiàn)一個字詞可以有兩種解讀的情況。
使用解決方案領(lǐng)域的命名
用程式設(shè)計師都懂的名稱命名。
使用問題領(lǐng)域的命名
若問題較特殊沒有程式設(shè)計師都懂的名稱,可使用相關(guān)領(lǐng)域的名稱命名。
添加有意義的上下文資訊
看到firstName、lastName、street、houseMumber、city、state及zipcode ,讀者會知道是地址。如果只有看到 state 會不知道是不是地址。
可用字首增加上下文資訊,例如addrFirstName、addrLastName、addrState。
或是產(chǎn)生一個地址Address類別。
Listing 2-2 針對Listing 2-1進行重構(gòu)改進,新增GuessStatisticMessage類別,將原本的函數(shù)拆成多個函數(shù),增加程式的可讀性。
別添加無理由的上下文資訊
作者舉個範(fàn)例豪華版的加油站(Gas Station Deluxe)應(yīng)用程式,在每個類別都加上GSD字首,作者認為加上GSD是畫蛇添足。
總結(jié)
命名難點在於需要良好的描述技巧與共通的文化背景,這是訓(xùn)練上的問題。
因為擔(dān)心其他開發(fā)者反對,一般人會放棄重新命名。
作者建議重新命名可增加程式碼可讀性,別讓重新命名阻礙前進。