最近有稍微跟幾位不同的朋友討論到相關的問題。這些問題本質上都是在探討怎麼讓程式碼更好維護,而其中一個問題是命名方法。我的看法是:
命名很重要,但是可能沒有你想像中的重要
Naming 跟 Cache invalidation 是 Computer Science 最大的兩個問題
相信大家都有多少聽過這句話,問題的核心是:怎麼用最少的資源來做最多的計算,其中資源包含了維護成本。那麼命名在這個命題中是扮演了怎麼樣的角色呢?
命名能幫助思考,畢竟人類之所以可以主宰地球靠的就是語言為工具的溝通能力。不過單純命名是沒有辦法輕易解決非常複雜的問題。常常會發生的問題是:我們對於新事物的觀念無法用非常精確的詞彙描述。其中一種解決方案是 Context,也就是在不同語境下,我們對同一個詞彙的意義是不同的。舉例來說,當我們在講 generic 這個詞的時候,在不同程式語言指的其實不一定是同一個觀念:
Observable<Cat>
的 class再舉另外一個程式語言裡的例子:map
function
基本上現在大部分的 language (或其 library) 都有這樣的一個 function,只是 syntax 有些許不同。而上面用的是 Haskell 的 type annotation,基本上是這樣讀的:
(a -> b)
:第一個參數,是一個 function 吃 type a 回傳 type bf a
:第二個參數,你可以想像成包有一個 type a 的「包裝」type,其中這個包裝本身是一個變數:ff b
:這個 function 的回傳值,跟 f a
類似所以 map 就是一個 function,其中的第一個參數是一個 function,然後用這個 function 來把 f a
裡面的 a 全部 "transform" 成 b,但基本上不改變 f
本身。
光這樣講應該會非常沒有感覺,所以讓我們看看這會怎麼用
[1, 2, 3]
裡面全部都加一,變成 [2, 3, 4]
null
來說就沒有作用。而 2
會被加一 3
。你可能會覺得 map 在命名上有些詭異。
第一,map 對於不同的 type 意義是不同的,不熟的人第一次接觸的時候會覺得「為什麼這個要叫做 map function?它就作用在 array 啊,為什麼不叫它 transformElementsBy
?」但問題是,map function 是可以作用在不同的 type/class 上,如果真的命名成 transformElementsBy
那對於 array 以外的 type 來說就很奇怪。
第二,這個 type signature 上面都是單一字母,就是大家最常說的糟糕命名法。但跟第一點一樣,如果你今天作用的對象並不是 array,命名成 elem/item 就可以說是莫名其妙。
最終你會發現,當你的程式中出現了足夠抽象的架構,尤其是對於這種超多功能的 function 來說,你會很難找到一個很完美的命名。除非你對這個結構熟悉,名稱對你才有意義,然後你才會知道該怎麼命名。而且在某些極端的狀況下,你根本就不知道該用什麼字去命名它。但其實並不是你不夠了解你的架構,而單純只是你不知道怎麼命名。
不同的 type 意味著不同的 context,所以我們可以用同樣的 function name 來代表不同的實作,這省去了我們要不斷重新取名的麻煩,也加強了 code reuse 的機會。當相對來說,不熟悉這個抽象概念的人很有可能會覺得這個命名可讀性非常差,但原因其實並不是「命名」而是「抽象理解」的缺乏。
根據時間的推移,有可能一個變數的命名可能經過更迭,早已偏離當初命名的初衷。不是說命名不重要,而是跟 comments 一樣,它有可能實際上並不反映程式碼當下的邏輯。
假設今天有個變數,一開始命名為 product,指的是某個商品,而經過一段,這個變數再也不是存 product 而是 order,而其中因為包含著商品,當初改這段程式碼的人並沒有花時間去改名。所以你可能看到的是 product 但實際上是 order。
這個例子中的問題雖然看似是命名問題,但真正的問題在於如何驗證修改前後的程式邏輯。雖然變數名稱並不正確,但實際上的邏輯是正確的。再舉另一個常見的例子:
"item" 可能指的是 product item 或 order item,為了區分這兩者,一個方法是分別取名為 productItem 跟 orderItem,但如果今天有個共用的多型函式,可以作用在這兩個物件上,那你會怎麼命名呢?
所以真正的重點不是在「命名」,而在於「驗證」。
統整以上兩小節:
抽象理解相信各位工程師都已經很有感覺了。那麼「驗證」實務上來說又有哪些方法呢?
如果今天你的驗證手段只有 1,當然那命名就極為重要。
反過來說如果你有 2, 3,那相對來說命名得好不好就顯得沒這麼重要。搞不好你的同事也不想認真幫你 review,反正他要忙他的,就直接讓你過。但自動化的程式,只要你 CI 設定好,可信賴程度自然不是單純 code review 比得上的。
軟體工程其實就是各種 tradeoff,如果花太多時間在斟酌命名,反而會少了建立其他自動驗證機制的時間。而我的觀點是:如果你今天自動驗證做得非常好,命名得好不好都只是錦上添花而已。
那麼怎麼樣才是最好的「驗證」方式呢?這應該可以留作下一篇的主題。