2013年4月24日 星期三

Code Complete II《軟體建構之道 2》#11 讀書心得與整理

第十一章 變數名稱的力量
The Power of Variable Names



11.1 選擇良好名稱的注意事項Considerations in Choosing Good Names

一個變量和一個變量的名字本質上是相同的東西
原文: a variable and a variable’s name are essentially the same thing
變數的一切,取決於變數名稱。
下列這個範例,是銀行要出信用卡帳單給客戶的程式碼。
請看看哪一個範例比較看得懂
//Java Example of Poor Variable Names
x = x - xx;
xxx = aretha + SalesTax( aretha );
x = x + LateFee( x1, x ) + xxx;
x = x + Interest( x1, x );
//Java Example of Good Variable Names
balance = balance - lastPayment;
monthlyTotal = NewPurchases + SalesTax( newPurchases );
balance = balance + LateFee( customerID, balance ) + monthlyTotal;
balance = balance + Interest( customerID, balance );

最重要的命名注意事項
The Most Important Naming Consideration

變數名稱最重要的是「可以完整而正確的描述」所呈現的形體
  • 容易閱讀
  • 無難猜的縮寫
  • 無歧義
  • 不會搞混
  • 好記
常見的問題:好記,但太長。

我的見解:
好的名稱通常需要形容詞和名詞的組合,並且類似currentData的方式取名。
而不好的名稱,通常是程式設計師本身的素養不夠而使用一瞬間的直覺(也許是懶得打太多字)為取名的依據。

問題導向
Problem-Orientation

一個「好記」的名字,表達的通常是「問題」而不是「答案(解決方案)」
一個好名字,表達的通常是"what"而不是"how"
所以,一個好的名字,在於反映"What is the problem",而不是"How the solutions work"

最佳命名長度
Optimum Name Length

過短: 訊息不足
過長: 難以輸入、程式看起來的會亂亂的

平均長度10~16 個字元: 除錯的心力,降到最低
平均長度08~20 個字元: 幾乎一樣(同上)好除錯
雖然如此,也並不代表你應該要將所有的變數都限制在09~15 或 10~16 個字元

Scop對變數名稱的影響
The Effect of Scope on Variable Names

短名稱也有必要性!
語意:表達「臨時變數」+「使用的Scop很有限」

長名稱: 較少使用、全域變數
短名稱: 迴圈變數、區域變數
由於防禦性程式設計原則,也要避免常用短名稱

對於命名全域的變數,使用修飾詞
  1. 對於有支援namespace的語言: 使用namespace管理
  2. 不支援namespace的語言: 命名時加上修飾詞
語意:全域的Var

變數名稱中的計數專用修飾詞
Computed-Value Qualifiers in Variable Names

Total、Sum、Average、Max、Min、Record、String或Pointer...等
請放在名稱末端,當作修飾詞。
語意:誰誰誰...(Var)的Total之類的

優點:
  1. 避免佔用變數命名的前面,變數命名的前面提供變數意義(理解變數的目的)
  2. 避免出現totalRevenue和revenueTotal的混淆(語意相同,當作不同)。
  3. 有優雅對稱性的命名規則
  4. 改善可讀性、維護更容易

常見的成對的變數名稱
Common Opposites in Variable Names

參考:《代码大全》的笔记-7.3 Good Routine Names


11.2 為特定資料種類命名
Naming Specific Types of Data

(Types of Data ≠ Data Type)

命名迴圈的索引變數
Naming Loop Indexes

  • 最常見的名字是i, j, k 
    1. 在小迴圈內用的變數:
      //Java Example of a Simple Loop Variable Name
      for ( i = firstItem; i < lastItem; i++ )
      data[ i ] = 0;
  • 避免使用i, j, k,而使用更有意義的名稱
    1. 迴圈外也要用的變數:
    2. //Java Example of a Good Descriptive Loop Variable Name
      recordCount = 0;
      while ( moreScores() )
      {
      score[ recordCount ] = GetNextScore();
      recordCount++;
      }
      something = recordCount;
      //...
    3. 在長迴圈內/巢狀迴圈用的變數:
    4. //Java Example of Good Loop Names in a Nested Loop
      for ( teamIndex = 0; teamIndex < teamCount; teamIndex++ ) {
      for ( eventIndex = 0; eventIndex < eventCount[ teamIndex ]; eventIndex++ ) {
      score[ teamIndex ][ eventIndex ] = 0;
      }}

命名狀態變數
Naming Status Variables


取一個比旗標更好的狀態變數名稱
狀態命名永遠不應該有"flag"這個名字
if ( printFlag == 16 )
statusFlag = 0x80;
誰知道printFlag 和16 是什麼?誰知道statusFlag和0x80是什麼?
又是什麼在等於什麼?
enum ReportType {
ReportType_Daily,
ReportType_Monthly,
ReportType_Quarterly,
ReportType_Annual,
ReportType_All
};
if ( reportType == ReportType_Annual )
//...
const int CONTROL_CHARACTER = 0x80;
characterType = CONTROL_CHARACTER;
這樣就好懂很多。

當你終於猜中(figuring out)一段程式碼後,請考慮重新命名這些變數。你可以推理猜測奪命懸案中的神秘兇手,不過沒有必要對程式碼這麼做,它們應該要設計得更好懂。

命名臨時變數
Naming Temporary Variables

最常見的名字是temp, x
暫存變數通用出現在
  1. 存放計算的中繼結果(hold intermediate results of calculations)
  2. 作為暫時符號(temporary placeholders)
  3. 存放內部整理的值(to hold housekeeping values)
「臨時變數」=程式設計師尚未完全了解問題

對臨時變數保持懷疑的態度
//C++ Example of an Uninformative “Temporary” Variable Name
// Compute roots of a quadratic equation.
// This assumes that (b^2-4*a*c) is positive.
temp = sqrt( b^2 - 4*a*c );
root[0] = ( -b + temp ) / ( 2 * a );
root[1] = ( -b - temp ) / ( 2 * a );
temp其實不應該叫temp吧?而且temp真的是個什麼都無法呈現的名字,要有信心,一定有更好的名字。
//C++ Example with a “Temporary” Variable Name Replaced with a Real Variable
// Compute roots of a quadratic equation.
// This assumes that (b^2-4*a*c) is positive.
discriminant = sqrt( b^2 - 4*a*c );
root[0] = ( -b + discriminant ) / ( 2 * a );
root[1] = ( -b - discriminant ) / ( 2 * a );
這樣就好多了。

命名布林變數
Naming Boolean Variables


牢記典型的名稱
  • done: 完成
  • error: 錯誤
  • found: 已找到
  • success/ok: 已完成

讓變數名稱有真假的隱喻
status = true;
sourceFile = true;
誰知道這是什麼鬼?
statusOk = true;
sourceFileFound = ture;
這就好多了

IsXX的命名方式(isDone?isError?isFound?isProcessingComplete?)
用true和false來回答問題
  • 優: 避免出現isStatus、isSourceFile這種鬼東西。
  • 缺: if ( isError) 比 if (error)難懂一點點。
我覺得還好,所以有在用,而且使用在回傳值為bool的函數命名。

命名列舉型別
Naming Enumerated Types

使用前綴詞,確保型別成員屬於同一組。

對於列舉型別,有兩種看法
  1. 它是使用者定義型別: 命名要加前綴字
  2. 它是常數: 命名要和常數一樣
  3. 它是類別(有些語言(Java)支援這種用法)
  4. enmu Color{ Color_Red, ...}
    Color.Color_Red
    這種情況,前綴詞就沒什麼用處。

命名常數
Naming Constants

const int FIVE = 5;
沒有人這樣用吧?
命名請用常數代表的含義,而非常數代表的值。


11.3 命名原則(慣例)的力量
The Power of Naming Conventions

抗拒命名原則的正當理由: 僵化、無效率、阻礙創意、阻礙程式品質
但是, 有效的標準是你隨時可用的強大的工具之一(Effective standards are some of the most powerful tools at your disposal.)

為什麼要有命名原則    Why?
Why Have Conventions?

透過一個全面性的決策,擁有一個命名原則,可以讓你專注在更重要的程式的特性,而不是命名這種小地方
更快速的了解不常見的變數,可能的目的
提高閱讀程式碼的速度
避免pointTotal和totalPoint的混用
彌補程式語言的弱點
強調相關相目之間的關係

命名原則是提供程式碼一種有彈性的結構,讓你擔心的事變少。

何時該使用命名原則    When?
When You Should Have a Naming Convention

多位程式設計師合作一個專案
程式會交給別人維護
其它的人會看你的程式碼
程式規模超大,無法一次記住全部(一個人包山包海)
自己隔很久(數週至數月)再看一次
程式有不常見的術語,而且需要用命名原則將它標準化

正式的程度    How?
Degrees of Formality

從巴別塔之後,這也許是一個福音。
    寫程式的人數×程式大小×程式預估壽命=正式度
非正式的命名原則,可能只是「使用有意義的名稱」這麼簡單


11.4 非正式命名原則(慣例)
Informal Naming Conventions


無關語言的一個公定原則
Guidelines for a Language-Independent Convention

變數variableName
函數FunctionName()
區別類別和物件
類別物件
1開頭字母大寫Widget開頭字母小寫widget
2命名全大寫WIDGET同1widget
3開頭加綴字t_t_Widget命名大寫開頭Widget
4同1Widget開頭加綴字aWidget
5同1Widget深入思考、長命名employeeWidget
全域變數g_variableName
成員變數m_variableName
型別定義(typedef, class)t_variableName, 全大寫
常數c_constVariableName, 全大寫
列舉e_enmuVariableName, E_enmuVariableName, 前綴字
在不支援唯讀的語言中,標識唯讀(常數)


格式化命名
一致命名格式,可增加可讀性
╳←GYNMASTICSPOINTTOTAL
╳←gymnasticspointtotal
○←gymnasticsPointTotal
○←gymnastics_point_total

特定語言的公定原則
Guidelines for Language-Specific Conventions

Java的命名原則
Java Conventions

C++的命名原則
C++ Conventions

C語言的命名原則
C Conventions

Visual Basic的命名原則
Visual Basic Conventions
(請看11.5)

混合語言程式設計注意事項
Mixed-Language Programming Considerations

示範命名原則
Sample Naming Conventions
制訂命名原則,要知道變數包含三個資訊
  1. 變數代表什麼?
  2. 用哪一種資料型態表示?
  3. Scop多大?


11.5 標準前綴詞
Standardized Prefixes

「匈牙利命名法」正是此方法的最佳範例

標準前綴詞有兩種
  1. 使用者定義型別縮寫
  2. 語意前綴詞

使用者定義型別縮寫
User-Defined–Type (UDT) Abbreviation

定義「標識命名物件或變數的資料種類」
在此的「資料種類」指的不是「程式語言的資料型別」(為匈牙利命名法平反)
ex: 文件、段落、畫面區域、視窗區域

語意前綴詞
Semantic Prefix

描述變數或物件的使用方式。

標準化前綴詞的優點
Advantages of Standardized Prefixes


優點:
標準化較多的名稱,要記的名稱會變少
精確定義一些較含糊的名字  min, frist, last...
名稱更加精簡
正確檢查型別  paReformat = docReformat ←這樣就看得出是不對的

缺陷:
在使用前綴詞的時候,忘了給予變數有意義的名稱,而降低可讀性

在此,繁/簡體的中文書都少了一段
是英文版的768~776行 ch11 p25(p302 of PDF)
我的翻譯如下:
因此,標準化前置詞的可讀性有時候不如一個更具描述性的名稱。
這個前綴詞的問題,不是一個充滿限制的缺陷。
沒有技術是銀彈,始終需要任何技術和個人紀律和判斷。
ipa是一個比i更好的變數名稱,它至少是朝著正確的方向一步。



11.6 建立易懂的短名稱
Creating Short Names That Are Readable


一般的縮寫原則
General Abbreviation Guidelines

使用字典中會用的標準縮寫
移除所有非第一個字母的母音
移除冠詞
使用第一個字母或前幾個
保留每個字的開頭或結尾的字母    i18n?
移除無用的後置字元(ed, ing)
保留重要的音節
...(一些類似的就沒有寫出來了)

同音縮寫
Phonetic Abbreviations


  • skating = sk8ing ( at = 8 )
  • highlight = hilite ( hight = hi, light = lite )
  • before = b4
  • execute = xqt

這種做法,就像是要猜個人化車牌,要像命案現場一樣慢慢推敲線索只為了一個變數名稱。

縮寫的心得
Comments on Abbreviations

不要只節省一個字元
一個字一個樣
    Number是No?還是Num?
使用能發音的字
避免會看錯或唸錯的組合
利用詞庫的同義字來解決縮寫重覆的問題
在程式碼中記錄縮寫對照表
在專案中建立「標準縮寫」

切記,程式碼對於讀者來說,比對作者來說更重要

11.7 要避免使用的名稱
Kinds of Names to Avoid

相同名稱,意思不同
不同名稱,意思相似
相似名稱,不同意思
聽起來類似,不同意思
避免使用數字
    使用陣列解決
避免拼字錯誤(或者不直覺的拼字)
避免易拼錯的字
避免只用大小寫區別變數名稱
避免多種自然(人類使用)語言
避免使用程式語言關鍵字
避免使用完全無關的名稱(或者全世界只有你一個人知道相關性的名稱)
避免使用難識別的字元

沒有留言:

張貼留言