大概在許多開發者的經驗裡面,最常感到被煩擾的事情可能不是想出或導入一個新的演算法,也不是解決嚇人的 legacy code。而是¬「如何命名新的變數」。為什麼「命名變數」這個聽起來這麼地基本,這麼像是大學生在印出人生第一個 “Hello World.” 後緊接而來的第二個問題,會像是鞋子裡的小石頭一樣地令人煩擾?
其實,變數的命名所牽涉到的問題其實都相當核心。幾個關鍵點像是初始化、作用範圍(scope)、持續性以及長期維護上必須面對的可讀性問題。當系統日益壯大後,當初這些小小的問題就會變得相當棘手。在終於變成一個 BUG 爆炸的當下,這一切在工程師眼裡可能看起來比跨年煙火還要盛大、還要孤寂。尤其是眼前的程式碼就像一盤義大利麵一樣,美麗且錯綜複雜的交織在一起。
所以說,如何在最開始編寫程式的時候,建立起保險機制絕對是首當其衝需要被關注的議題。以下筆記摘記自 Steve McConnell 的 Code Complete 一書所提到的變數原則裡面比較基本且必須的注意項目。
初始化
宣告變數的時候就先對變數作初始化。對於防範錯誤來說,這是相當保險的策略。比如說確保每一次呼叫子程式時,某個變數一定會被初始化。且宣告和定義的位置理想上應落在第一次使用該變數的附近。
在程式碼中,某些變數是程式中重要的索引或參照值所以不可更動。為了保持正確性,應該使用 const 來防止變數在初始化後,又被重新賦值。不過,既然有些變數不能被改動,也勢必會有需要不斷改動的變數。而這些時常需要改變的變數,例如累加器 (accumulator) 或是計數器 (counter) 中 i, j, k, sum, total 這類的變數,也常有忘記重置,也就是遺漏重新再賦值的情況。這些情形都是可以在宣告或賦值時特別注意的點。
作用範圍 (scope)
基本上,變數所存活的範圍就是在其所處的 scope 中。不同的語言對於 scope 的定義不同。
實務上來說,會希望變數本身的生存時間不要太長。更短的存活時間可以減少錯誤發生的機會。在編寫程式的過程中,如果跨度[1]太大或是存活時間[2]太長的話很容易就會忘記初始的目的,或是混淆功能。
如果能降低變數的存活時間,那麼在查找上,同一個範圍內相同的變數數量就會下降很多。維護的難度也就自然減低不少。
[1] 跨度指的是這次使用與下次使用同一變數之間的程式碼間隔
[2] 存活時間指的是某一變數在全部程式碼中出現的總範圍
降低作用範圍的原則
如同前述,如果事隔太遠就很容易忘記初始宣告的時候到底寫了什麼。所以在策略上,都應該避免在整個程式碼的最開頭處宣告一堆變數。在迴圈開始前才作初始化宣告,或是變數快要被使用前才替其賦值。其他接手的維護者在檢視這段程式碼時,才不會疲於來回滑動頁面。這使得維護有更好的效率。
在可以做到的範圍內,盡量降低該區間所需要的變數數量。範例中,便從區間內同時使用五個變數,改為區間內分別使用三個及兩個變數。程式碼也更加容易理解。
變數只用在單一用途
有些時候,很容易因為方便而直接使用 temp, tmp, x 作為暫存值。但過度使用就會因為分辨不出真實的意義,而增加了維運的時間成本。
使用更詳細的命名方法也可以創造出不同情境下所需的特殊的條件判斷。例如:使用 pageCount 來表示頁面的計數,但若為其為負值,也可以成為判斷有錯誤發生指標。或是使用 goodsId 來給存貨編號。當編號超過某個數值後,可以判別哪個號碼以前的是過期的物品。
命名的規則
命名的注意事項
太過冗長的變數名稱也會使得可讀性下降,下面提及的是比較常遇到的情形與可以採納的想法:
1. 問題導向
把命名變數的邏輯從解決方法(how)改變成可以反映問題(what)的方法。比如說,在計算類的軟體中,使用 calcVal 可能比使用 sum 來得更好。statusBitCode 大概也會比 flag 來得更意義明朗一些。
2. 名稱長度與規則
在命名的過程中也要避免同時出現 acctNum 以及 acctNo 這樣容易混淆的變數名稱。這依賴的是專案開始前訂定命名方針,以及使用共用的命名字典來確認是否有混淆字或重複使用相同變數名稱。且根據研究指出,變數名稱大約在8-20個字母左右會是最容易閱讀的長度。所以過短或是太過詳盡的命名都可能帶來不良影響。
3. 作用範圍的影響
但在使用 lambda 的時候,較短的變數名稱也隱含著較短的生命週期的意味。所以在某些變數生命週期極短的情況下,使用非常短或是單字母作為變數也能使得程式碼看起來更簡潔、易讀,同時也僅有較低的風險需要面臨上述提及的命名問題。
4. 修飾詞
將修飾詞 (像是 total, sum, average, max, min) 放於名詞之後。將名詞置於前方可以避免歧義或順序顛倒,降低混淆與模糊空間。
比較特別的例子是 num,若放在名詞前面通常代表的是總數的概念,放名詞後則是代表序號。
5. 布林變數
常見的布林變數的名稱有 done, found, error, success/ok。這樣的詞彙內已經包含著 true/false 的概念,使程式碼更簡潔。有些人會使用 is 作為布林變數的開頭,這使得模糊不清的名稱可以被避免(如 isStatus?)。但若與 if(found) 相比,if(isFound) 的可讀性相對又差了一些。
總結來說,這些命名規則的產生是為了可以讓工程師們更專注在程式碼的特徵之上。在編寫程式時,降低思考斷裂或鴻溝。而且擁有共同的命名規範能使得眾人更快地在新舊專案中上手,減少猜想原先命名意涵的時間。在規範之下,變數數量也不至於大量膨脹。團隊成員們也會有一致性更高的程式碼產出。
延伸閱讀
好讀版網址:https://hackmd.io/@ASTN/SkrlvQeLO
Syntax Highlighter:https://highlight.hohli.com/index.php