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