以中國傳統的孔子和老子的思想來分析忍者程式碼

技術漫談發表於2019-12-13

以中國傳統的孔子和老子的思想來分析忍者程式碼

忍者程式碼

學而不思則罔,思而不學則殆。

—— 孔子

過去的程式設計師忍者使用這些技巧,來使程式碼維護者的頭腦更加敏銳。

程式碼審查大師在測試任務中尋找它們。

一些新入門的開發者有時候甚至比忍者程式設計師能夠更好地使用它們。

仔細閱讀本文,找出你是誰 —— 一個忍者、一個新手、或者一個程式碼審查者?

注意:檢測到諷刺意味

許多人試圖追隨忍者的腳步。只有極少數成功了。

簡潔是智慧的靈魂

把程式碼儘可能寫得短。展示出你是多麼的聰明啊。

在程式設計中,多使用一些巧妙的程式語言特性。

例如,看一下這個三元運算子 '?'

// 從一個著名的 JavaScript 庫中擷取的程式碼
i = i ? i < 0 ? Math.max(0, len + i) : i : 0;
複製程式碼

很酷,對嗎?如果你這樣寫了,那些看到這一行程式碼並嘗試去理解 i 的值是什麼的開發者們,就會有一個“快活的”的時光了。然後會來找你尋求答案。

告訴他短一點總是更好的。引導他進入忍者之路。

一個字母的變數

道隱無名。夫唯道善貸且成。

—— 老子(道德經)

另一個提高程式設計速度的方法是,到處使用單字母的變數名。例如 abc

短變數就像森林中真正的忍者一樣,一下就找不到了。沒有人能夠通過編輯器的“搜尋”功能找到它。即使有人做到了,他也不能“破譯”出變數名 ab 到底是什麼意思。

……但是有一個例外情況。一個真正的忍者絕不會在 "for" 迴圈中使用 i 作為計數器。在任何地方都可以,但是這裡不會用。你隨便一找,就能找到很多不尋常的字母。例如 xy

使用一個不尋常的變數多酷啊,尤其是在長達 1-2 頁(如果可以的話,你可以寫得更長)的迴圈體中使用的時候。如果某人要研究迴圈內部實現的時候,他就很難很快地找出變數 x 其實是迴圈計數器啦。

使用縮寫

如果團隊規則中禁止使用一個字母和模糊的命名 — 那就縮短命名,使用縮寫吧。

像這樣:

  • list -> lst
  • userAgent -> ua
  • browser -> brsr
  • ……等

只有具有真正良好直覺的人,才能夠理解這樣的命名。儘可能縮短一切。只有真正有價值的人,才能夠維護這種程式碼的開發。

Soar high,抽象化。

大方無隅,
大器晚成,
大音希聲,
大象無形。

—— 老子(道德經)

當選擇一個名字時,儘可能嘗試使用最抽象的詞語。例如 objdatavalueitemelem 等。

  • 一個變數的理想名稱是 data 在任何能用的地方都使用它。的確,每個變數都持有 資料(data),對吧?

    ……但是 data 已經用過了怎麼辦?可以嘗試一下 value,它也很普遍。畢竟,一個變數總會有一個 值(value),對吧?

  • 根據變數的型別為變數命名:strnum……

    嘗試一下吧。新手可能會詫異 — 這些名字對於忍者來說真的有用嗎?事實上,有用的!

    一方面,變數名仍然有著一些含義。它說明了變數內是什麼:一個字串、一個數字或是其他的東西。但是當一個局外人試圖理解程式碼時,他會驚訝地發現實際上沒有任何有效資訊!最終就無法修改你精心思考過的程式碼。

    我們可以通過程式碼除錯,很容易地看出值得型別。但是變數名的含義呢?它存了哪一個字串或數字?

    如果思考的深度不夠,是沒有辦法搞明白的。

  • ……但是如果找不到更多這樣的名字呢? 可以加一個數字:data1, item2, elem5……

注意測試

只有一個真正細心的程式設計師才能理解你的程式碼。但是怎麼檢驗呢?

方式之一 —— 使用相似的變數名,像 datedata

盡你所能地將它們混合在一起。

想快速閱讀這種程式碼是不可能的。並且如果有一個錯別字時……額……我們卡在這兒好長時間了,到飯點了 (⊙v⊙)

智慧同義詞

最難的事情是在黑暗的房間裡找到一隻黑貓,尤其是如果沒有貓。

—— 孔子

同一個 東西使用 類似 的命名,可以使生活更有趣,並且能夠展現你的創造力。

例如,函式字首。如果一個函式的功能是在螢幕上展示一個訊息 — 名稱可以以 display… 開頭,例如 displayMessage。如果另一個函式展示別的東西,比如一個使用者名稱,名稱可以以 show… 開始(例如 showName)。

暗示這些函式之間有微妙的差異,實際上並沒有。

與團隊中的其他忍者們達成一個協議:如果張三在他的程式碼中以 display... 來開始一個“顯示”函式,那麼李四可以用 render..,王二可以使用 paint...。你可以發現程式碼變得多麼地有趣多樣呀。

……現在是帽子戲法!

對於有非常重要的差異的兩個函式 — 使用相同的字首。

例如,printPage(page) 函式會使用一個印表機(printer)。printText(text) 函式會將文字顯示到螢幕上。讓一個不熟悉的讀者來思考一下:“名字為 printMessage(message) 的函式會將訊息放到哪裡呢?印表機還是螢幕上?”。為了讓程式碼真正耀眼,printMessage(message) 應該將訊息輸出到新視窗中!

重用名字

始制有名,
名亦既有,
夫亦將知止,
知止可以不殆。

—— 老子(道德經)

僅在絕對必要時才新增新變數。

否則,重用已經存在的名字。直接把新值寫進變數即可。

在一個函式中,嘗試僅使用作為引數傳遞的變數。

這樣就很難確定這個變數的值現在是什麼了。也不知道它是從哪裡來的。目的是提高閱讀程式碼的人的直覺和記憶力。一個直覺較弱的人必須逐行分析程式碼,跟蹤每個程式碼分支中的更改。

這個方法的一個進階方案是,在迴圈或函式中偷偷地替換掉它的值。

例如:

function ninjaFunction(elem) {
  // 基於變數 elem 進行工作的 20 行程式碼

  elem = clone(elem);

  // 又 20 行程式碼,現在使用的是 clone 後的 elem 變數。
}
複製程式碼

想要在後半部分中使用 elem 的程式設計師會感到很詫異……只有在除錯期間,檢查程式碼之後,他才會發現他正在使用克隆過的變數!

經常看到這樣的程式碼,即使對經驗豐富的忍者來說也是致命的。

下劃線的樂趣

在變數名前加上下劃線 ___。例如 _name__value。如果只有你知道他們的含義,那就非常棒了。或者,加這些下劃線只是為了好玩兒,沒有任何含義,那就更棒了!

加下劃線可謂是一箭雙鵰。首先,程式碼變得更長,可讀性更低;並且,你的開發者小夥伴可能會花費很長時間,來弄清楚下劃線是什麼意思。

聰明的忍者會在程式碼的一個地方使用下劃線,然後在其他地方刻意避免使用它們。這會使程式碼變得更加脆弱,並提高了程式碼未來出現錯誤的可能性。

展示你的愛

向大家展現一下你那豐富的情感!像 superElementmegaFrameniceItem 這樣的名字一定會啟發讀者。

事實上,從一方面來說,看似寫了一些東西:super..mega..nice..。但從另一方面來說 — 並沒有提供任何細節。閱讀程式碼的人可能需要耗費一到兩個小時的帶薪工作時間,冥思苦想來尋找一個隱藏的含義。

重疊外部變數

處明者不見暗中一物,
處暗者能見明中區事。

—— 關尹子

對函式內部和外部的變數,使用相同的名稱。很簡單,不用費勁想新的名稱。

let user = authenticateUser();

function render() {
  let user = anotherValue();
  ...
  ...許多行程式碼...
  ...
  ... // <-- 某個程式設計師想要在這裡使用 user 變數……
  ...
}
複製程式碼

在研究 render 內部程式碼的程式設計師可能不會注意到,有一個內部變數 user 遮蔽了外部的 user 變數。

然後他會假設 user 仍然是外部的變數然後使用它,authenticateUser() 的結果……陷阱出來啦!你好呀,偵錯程式……

無處不在的副作用!

有些函式看起來它們不會改變任何東西。例如 isReady()checkPermission()findTags()……它們被假定用於執行計算、查詢和返回資料,而不會更改任何他們自身之外的資料。這被稱為“無副作用”。

一個非常驚喜的技巧就是,除了主要任務之外,給它們新增一個“有用的”動作。

當你的同事看到被命名為 is..check..find... 的函式改變了某些東西的時候,他臉上肯定是一臉懵逼的表情 — 這會擴大你的理性界限。

另一個驚喜的方式是,返回非標準的結果。

展示你原來的想法!讓呼叫 checkPermission 時的返回值不是 true/false,而是一個包含檢查結果的複雜物件。

那些嘗試寫 if (checkPermission(..)) 的開發者,會很疑惑為什麼它不能工作。告訴他們:“去讀文件吧”。然後給出這篇文章。

強大的函式!

大道泛兮,
其左可右。

—— 老子(道德經)

不要讓函式受限於名字中寫的內容。拓寬一些。

例如,函式 validateEmail(email) 可以(除了檢查郵件的正確性之外)顯示一個錯誤訊息並要求重新輸入郵件。

額外的動作在函式名稱中不應該很明顯。一個真正的忍者會使它們在程式碼中也不明顯。

將多個動作合併到一起,可以保護你的程式碼不被重用。

想象一下,另一個開發者只想檢查郵箱而不想輸出任何資訊。你的函式 validateEmail(email) 對他而言就不合適啦。所以他不會找你問關於這些函式的任何事而打斷你的思考。

總結

上面的所有“建議”都是從真實的程式碼中提煉而來的……有時候,這些程式碼是由有經驗的開發者寫的。也許比你更有經驗 ;)

  • 遵從其中的一丟丟,你的程式碼就會變得充滿驚喜。
  • 遵從其中的一大部分,你的程式碼將真正成為你的程式碼,沒有人會想改變它。
  • 遵從所有,你的程式碼將成為尋求啟發的年輕開發者的寶貴案例。

本文首發於微信公眾號「技術漫談」,歡迎微信搜尋關注,訂閱更多精彩內容。


現代 JavaScript 教程:開源的現代 JavaScript 從入門到進階的優質教程。React 官方文件推薦,與 MDN 並列的 JavaScript 學習教程

線上免費閱讀:zh.javascript.info


掃描下方二維碼,關注微信公眾號「技術漫談」,訂閱更多精彩內容。

以中國傳統的孔子和老子的思想來分析忍者程式碼

相關文章