在編程實踐中定義變量時,咱們所能控制的無非兩點:變量類型與變量名。某種程度上,這二者分別考驗的實際上是開發者的數學水平與語文水平。在今天,即使已經有了很是高大上的類型系統,「名存實亡」的變量名仍然常常能對開發者形成困擾。那麼,咱們有什麼理論能用來指導變量命名呢?編程
在計算機科學的萌芽時代,變量名和變量類型之間並無明確的界限。好比,著名的「匈牙利命名法」提倡的就是把屬性 + 類型 + 描述一股腦地寫進變量名:例如 const int max 就對應 c_i_max 的名字。編程語言發展到今天,這種實踐看起來天然就很彆扭了。對於如今的主流編程語言,咱們大能夠認爲,它們中的變量名就應該是純粹的「對變量的描述」。數組
那麼,怎麼樣描述好一個變量呢?這時,咱們的關注點其實已經從天然科學中的類型理論轉移到人文科學中的文學創做了 :) 在給變量取名時,咱們實際上要作的是言簡意賅地描述清楚一個抽象的東西,這其實並非件容易的事情——「名字」但是萬惡的天然語言的範疇裏的東西,試問列位能在白板上手寫 bug-free 紅黑樹的大神們,可曾用母語寫出過滿分的高中做文?bash
因此,天然語言在編程中其實發揮着很是重要的做用——畢竟代碼是寫給人看的。筆者相信,每一個正常的閱讀者,都會在潛意識裏用從小耳濡目染的天然語言來理解代碼邏輯,而後纔是套用(也許是培訓班昨天才教的)編程語言語法規則來考量細節。這裏,某些標榜着簡潔但充斥着大量符號的編程語言就能夠做爲反例:用它們寫出的代碼也許對於熱愛推導公式的 Geek 來講很是友好,但普通人第一眼看上去就是天書。固然,矯枉過正的例子也不是沒有:對於一些鼓勵超長變量名的語言,維護它們的代碼……多少會有些捏着鼻子的感受。app
到此,咱們已經總結出了變量命名的若干微妙之處:編程語言
聽起來是否是很主觀,很難量化呢?這就引出了咱們的主題:雖然命名難以量化,但你能夠依據天然語言的語法,來整理出一些通用的規則呀 :)函數式編程
語法是筆者高中時代比較反感的東西——你不學它,光憑「語感」也能中個八九不離十;你學了它,反而可能稀裏糊塗套錯場景……不過,語法其實很像天然語言的類型系統,它與編程語言中相應的概念有着很微妙的聯繫。好比,JavaScript 中的關鍵字,就能夠根據語法中的詞性來這樣粗略地分類:函數
名詞
function var class
動詞
import export extends return break continue
delete switch new try catch throw yield
介詞
for in else
連詞
if while
代詞
this
複製代碼
是否可以注意到動詞明顯地比名詞更多?並且,編程語言中還有很多介詞 / 連詞這類的虛詞來表達控制流。相比之下,HTML 則幾乎就是名詞的天下:吃我 head body form button 啦!HTML 無非是一堆名詞的堆疊嵌套,相比存在着複雜邏輯的編程語言來講,較少遇到與命名相關的維護性問題。那麼,在維護編程語言的代碼時,有哪些天然語言的規則可以有助於提升可讀性和可維護性呢?這裏筆者總結了這麼幾點:工具
讓咱們逐條看一下吧 :-Dfetch
前面咱們已經知道,詞彙的詞性和變量的類型有着一些微妙的關係。在命名時,保持這種關係的匹配有利於加強可讀性。注意,這並非像匈牙利命名法那樣把類型寫入變量名,而是根據類型來選擇更契合的變量名:ui
isSomething
/ hasSomething
的形式。這其實很是接近天然語言裏的陳述句——陳述句有着確定和否認的形式,這就暗合了布爾值的 true
和 false
。apples
在天然語言裏就暗示着有不止一個 apple
,這和數組的思惟模型是很是契合的。UserModel
這樣的 class。別忘了,名詞和動詞都屬於實詞,不過兩者一個更加「面向對象」,一個更加「函數式」罷了。getUserData()
就能無縫地變成通順的 Please get user data
的祈使句了。固然,在函數式編程中,面向函數耍出的各類花樣,還能夠用各類虛詞來粉飾。譬如像 withState
這樣的名字,其中就暗含着對參數的「修飾」能力。再有各種回調的場景,也常見 on / before / after 這樣的介詞來代表相應動做的觸發時機。不一樣的編程語言和範式,在詞彙的選用上會有很是鮮明的區別。好比 Java 就是個名詞王國,而函數一等公民的語言裏,動詞的地位就高得多。這裏的風格建議是在什麼地方就按什麼地方的規矩來講話,這樣到哪都吃得開 XD
在有了 Lint 等格式化工具以後,項目裏的換行縮進通常都能獲得統一,這確實能消除代碼格式排版上的潦草感受。可是,許多項目裏仍然很常見這樣的地方:
loadUser
/ fetchUser
/ getUser
三個不一樣的函數,它們好像都是一回事,但是你敢隨便換着用嗎?elements.forEach(element)
,下面就是 elements.forEach(elem)
。一個 index 也能夠有 i
/ idx
/ inx
/ ind
四種不一樣的縮寫 :)$xxx
的格式聲明,新代碼上來就是個 __xxx
。其實這些問題單獨拿出來,都很難稱之爲嚴重,相應的解決方案應該也都是老生常談的了。但若是這樣的不一致性遍及項目的代碼庫,那麼再工整的空格縮進,也掩蓋不了代碼的雜亂感。這裏的一個非技術建議是創建 Code Review 機制:這些問題很難單純經過 Lint 工具來約束,提出 Review 意見與修改也並不難,關鍵在於流程:
固然了,Code Review 還有許多其餘的功效,這裏就再也不展開啦。
咱們已經提到過,直覺在編碼中發揮着潛移默化的做用。那麼,什麼叫作「反直覺」呢?譬如這麼些小地方:
if (!dataNotLoaded !== false)
的精妙邏輯,不過這時候恐怕很容易把本身和別人繞進去,尤爲是在與或條件多起來的時候 :-pdata.data
,該怎麼肯定其它代碼中用到的 data
變量指的是誰呢?得益於天然語言中豐富的抽象概念,總有一些變量名是很是「方便偷懶」的。好比,若是你實在想不通一個裝數據的變量該怎麼命名,那就叫它 data
啊!若是一個基類不知道叫什麼好,那就叫它 Base
唄!
其實,若是是由於「沒有想清楚該起什麼名字」而使用了這樣寬泛的概念,實際上就會在暗中欠下「沒有清晰正確的設計」的技術債。好比上面的行爲,在擴展或重構時極可能遇到這樣的問題:
data
,以致於不只無法簡單地查找與替換來重構,依賴 IDE 來改個變量名都要提心吊膽,不肯定影響範圍。core
/ base
/ common
的模塊,難以界定邊界在哪:core 到底管些什麼事,繼承它到底會帶來哪些反作用,而個人新函數要不要放在裏面呢?對於裝數據的變量來講,data
/ item
這樣的名字寫了和沒寫差很少——固然了,對於能夠多處複用的工具函數,這樣的命名徹底沒有問題。相對地,對於函數來講,各類具體的動做就比較容易描述清楚,不過你若是非要把全部的回調名一概寫成 callback
,那也不是不能夠……
其實上面的這些問題,不少只要在提交前 double check 一遍流程,就可以在自省中發現。這也是一個很是重要的技能:找出本身哪裏作得還不夠好,自己就是一種進步了。
對各類概念的抽象過程經常是編程中最有樂趣的地方之一,而好的變量命名無疑有助於將思惟過程更清晰地表達出來。在本文提出的觀點裏,變量的命名之難,與類型的強弱和類型系統的動態靜態並無直接的關係:數學大師的語文未必就足夠好,反之亦然。咱們也基於一些天然語言裏很是簡單的規則來提出了一些對編碼實踐的建議。不過,若是下次遇到 diss 你變量命名的 review 建議的時候,本文也能夠爲你提供一個有力的反擊論據:
咱們連編程語言的類型系統都很難徹底搞明白,更況且天然語言的呢?