良好程式碼的優點大同小異。
不好的程式碼的糙點卻各有巧妙之處。
前額葉皮質(prefrontal cortex, PFC)是額葉的前部。
一般會介紹它掌管人類的理性,讓人可以辛苦的努力生活著,而不是讓人朝向舒適、安逸的方向生活著。
不用前額葉命名指的是,命名很糙。
相信任何一本介紹關於程式碼品質的書,都會介紹到命名的重要性。
"the goodness or badness of a variable is largely determined by its name. "
--《Code Complete 2/e》, Ch11 The Power of Variable Names, 11.1 Considerations in Choosing Good Names
意思是「一個變數的表現如何,取決於它的命名。」
舉一些常見的無用命名,如果有縮寫,就一列在一起,不然就太長了
如果出現上述的命名,一定是沒有深刻的了解問題,一定是。
出現這樣的命名,我都反問
「哪個變數不是 value」
「哪個字串不是 str」
「哪個數字不是 number」
「number, num, No 唸唸看它們是不是有差別?」
「哪個函數不是 doSomething()」
「哪個物件沒有 info」
「你要解決的是什麼問題 (出現 tmp, temp, aa, cc),用說的給我聽」
如果出現無意義的前後綴,會看看上下文是不是有重複的字眼。
「好的程式要好猜,來自於好的命名。」
好猜中,一直是我個人對程式碼好壞的標準,這其實也是寫作中最重要的事。有些小說讓你好猜中後面的劇情,其實寫小說的人也許是個優秀的程式設計師 (笑)
有些標準可以參考[1]
好的命名,會幫助程式碼閱讀,加快除錯與維護的速度,降低維護成本提高工程師產值,若無法了解這一件事,別說你是一個「好的工程師」
以學語言來分析使用命名這件事,可以分成聽、說、讀、寫。寫程式就是使用「讀、寫」的技術,而這些其實很常見,往往忽略了語言的「聽、說」特性,很簡單的好命名測試方式: 「用唸的,聽聽看是不是可以唸?聽不聽得懂?」
在此就不像各家程式碼品質書上這樣的詳細介紹。
這個情況較常見,不管是什麼語言都可以使用。
而 boolean 並不只適用有型別的語言。
就連 C 語言(沒有 boolean) 都適用。
int isTrue = 1;
if (isTrue) {
//...
}
也就是說,只要符合特定概念,就可以使用該命名。不用管它是什麼程式語言。
(variable 是 class 的物件形式)
大多數的命名詞性,都是名詞、動名詞,總之是一種「定義」型的命名方式
與 class 或 variable 一起出現的等號運算式,正是一種 is
表現定義的方式。
用詞解釋
funciton 和 method 大多數使用的命名詞性是動詞或動詞片語[2]
getter/setter 要用 get/set 當前綴字 (不過現在有些語言會在 class 定義 getter/setter 並且隱含的自動執行)
學英文句型時,總是會學到的基本句型術語
用英文的句型來看,好的命名就會符合這樣的句型
S.V(O)
這種 object 配合 method 的做法,沒有被動式的動詞。
被動式
什麼時候使用被動式的語意呢? event 觸發時!!!
const button = document.querySelector('button')
button.addEventListener('click', callback)
在這時會說「當點擊按鈕時,執行 callback」,其實按鈕是「被點擊」。
在此 callback 的命名,會使用主動型態的動詞。(ex: createItem, deleteUser...)
好吧,有點牽強。(笑)
被動式的確很少拿來解式程式語言,一般看來是主動式的命名。
for (let i = 0; i < 10; i++) {
//...
}
在此使用 i
原因有兩個可能
index
的縮寫,是一個迴圈中的索引值iterate
的縮寫,指的是一種迭代的過程使用了 i 之後,依照數學慣例,接續在後面的字母就會延續一樣的概念使用,而不管字母本身的符號意義。
j 的意思,只是 i 的下一個
k 的意思,依此類推
這個惡名昭彰的匈牙利命名法,這一段要來說它的好與壞。
最初由 Charles Simonyi 發明,最早出現在微軟的 Windows 團隊。[3]
最初的用意
相信任何方法的立意都是良善,而不是要讓程式碼更不好。所以匈牙利命名法一開始也是為了解決問題而存在的。
在早期的 C 語言編譯器,不會進行型別檢查,對於 call by value 或 call by address 時,總是產生 bug,對於這個常見的問題,提出了解決方式。
使用方式:
p
當前綴所以,匈牙利命名法的前提就是以傳達資訊為主,不管名稱是不是可以唸出來[4]
長得像這樣
void *pvNewBlock(size_t size);
void *pvResizeBlock(void *pv, size_t sizeNew);
優點
在 C 語言的指標操作,常常會因為 *
符號而搞得不知道這變數是不是要加 *
加了會不會出錯,而感到心有疑慮的寫程式。
在下面的程式,只要把成對的 *
和 p
相抵,就可以推算出這一行是不是有錯誤的賦值行為。
*ppb = ppbNew;
// (*p)pb = ppbNew;
// 等效於 pb = pbNew;
pb = &b; // p 和 & 相抵消
b = psym->bLength //p 和 -> 抵消
這樣就可以在一行程式中指出可能會發生的錯誤。
(別忘了,還有寬字元和一般字元的問題,這就更需它了)
缺點
唸不出來(和上述的測試方式矛盾了!!)
犧牲可讀性
為什麼現在很少見?或之後被說得這麼難聽?
也許,有一陣子被濫用。
不過主要的原因,是後來的編譯器進步,會檢查型別並建議使用轉型,就不再需要這樣,犧牲可讀性的命名方式了。
匈牙利命名法的延伸應用,其實到現今還有在使用。常見的命名原則像「全域變數要加 g
當前綴」,但是匈牙利命名法給後來的時代什麼樣的啟示呢?
匈牙利命名法,當初之所以好用,是因為它利用命名彌補了語言本身的缺陷,但是因為語言進步了,這樣的命名法就不再適用了。
也就是說,當語言本身有什麼功能尚未有實作時,也許就會出現這樣的命名法。(亂世,就會出英雄的概念?)
將匈牙利命名法再精鍊一下,本質上它是使用前後綴的技巧,達到傳遞某種訊息。
BEM 是 Block, Element, Modifier 的縮寫,意思要依照這個順序命名,就可以找到 Selector 之間的相依性。
學習 BEM 前,先想想 CSS 這個標記語言有什麼缺陷?
這也許是它的特色啦,不過對於程式語言來說,對於有 scope 的程式語言來說,對於習慣使用有 scope 的程式語言開發者來說,這是非常不習慣的。(這句很重要,所以要強調)
那麼,在 CSS 中的任何命名方式,都是為了彌補這一點,盡可能的讓命名含有 scope 或區域化,不要造成全域污染而做的努力。
如果遇到任何 CSS 命名有需要加 g
(全域) 的前綴,請務必想起 CSS 的語言特性。(笑)
[1]: 《Code Complete 2/e》, Ch11 The Power of Variable Names, 11.1 Considerations in Choosing Good Names, The Most Important Naming Consideration
[2]: 《Clean Code》, Ch2 Meaningful Names, Method Names
[3]: Hungarian notation
[4]: 《Writing Solid Code》 What are those gobbledygook names
昨是今非。
10年後或許又會有人提出更新的理論,來否決目前當道的流行用法。
當資訊人累,老有新技術新理論提出永遠學不完。
不過這文寫的不錯,能接受多少決定了自己被洪流淘汰的速度。
感謝留言。