半熟前端

軟體工程師 / 台灣人 / 在前端的路上一邊探索其他領域的可能性

程式筆記

【筆記】易讀程式碼之美學

比較短的程式碼比較好嗎?

雖然減少程式碼會提高易讀性,但縮短讀程式碼的時間才是重點

表層結構

  1. 清楚的命名方法跟變數名稱

    • 方法不需要用 do
    • 選擇詞彙的時候不要用模擬兩可的名稱,ex: pop popItem
    • 在方法名稱加入更多的資訊
function getPage() {}
// 對方可能不知道 getPage 的實作方式?爬蟲? ajax?
function fetchPage() {}
// 可能比較清楚是用 ajax 的方式並且回傳 json。
  1. 找尋更明確的詞彙

    • send => deliver dispatch announce route
    • find => search extract locate recover
    • start => launch create begin open
    • make => create setup build generate add new compose

清楚明確比可愛更重要

  1. 就算是 tmp 變數,也可以提供多一點資訊。

    • tmpNumber
    • tmpFile
    • tmpUsrData
  2. 如果迴圈的 i,j 有意義,那麼就取個適當的名字。 ex: row col index

  3. 選擇具體的方法名稱。

  4. 如果變數具有單位,把單位放進去。

    • startSec
    • delayMs
  5. 重要屬性的變數命名

    • plainData
    • entryptedData
  6. 如果變數的作用範圍比較大的時候,用比較長(or 包含資訊較多)的變數是比較好的選擇,相反的如果只有幾行程式碼就結束,其他人可以馬上看出這個變數在做什麼,那麼就算用 alias 也沒關係。

不被誤解的名稱

  1. filter 是把東西 filter 掉,還是留下?
  2. min max 前綴
  3. 布林值
  4. computeData => 比較像執行一個耗費較大的 function

排版一致

  1. 符合排版一致
  2. 調整程式碼有的相似外觀
  3. 相關程式碼為一個段落。
  4. 註解的美學

為什麼排版那麼重要?第一個是其他人(或者是你)以後再看程式碼的時候,至少比較容易(也比較願意)看得懂,再來是你可以花更少的時間去理解你的 code 在幹麻,何樂不為?

代碼品質工具

  • eslint

  • stylelint

用方法消除混亂?

如果你發現你在做某一件事情感覺很混亂的時候,就該使用 method 包裝。

用精美不實的包裝來欺騙消費者是人類的本性(誤)。能不能欺騙別人不是重點,重要的是是否能夠欺騙你自己。連你自己都不尊重的 code 別人也不尊重的。

例如說:

assert(checkTime("12:00")) === "12:00"
assert(checkName("kalan", 20)) === { name: "kalan", age: 20 }
assert(checkPaid(20000, true)) === 20000

仔細觀察一下上面的 code ,不難發現它們都是在做一些同樣的事情,而且還有一些重複出現的字串。而且太長了,我們需要一點時間才能知道這幾行 code 在幹嘛。

這個時候就需要重構啦,我們可以用 method 把它包裝起來。

function checkValue(type, value) {
  if (type === "time") {
		assert(checkTime(value));
  }

  if (type === "name") {
    assert(checkName(value) === value;
	}

	if (type === "paid") {
		assert(checkPaid(value)) === value;
	}
}

// checkValue(type,    value);
//  					[string] [depend]
checkValue("name", kalan);
checkValue("time", 12:00);
checkValue("paid", 20000);

這樣一來 code 比較簡潔,易讀性也提升了! 再次強調,並不是比較精簡的程式碼就是好 code ,容易讓人理解的 code 才是好 code。 除了這些之外,還有一些好處:

  • 清楚呈現測試的部分。
  • 更容易加入其他測試!

照順序及段落區分:

落落長的程式碼不僅是別人,連自己都不會想看。在變數宣告、陳述式表達的時候,可以依照所做的行為不同拆分。這個寫文章也需要段落是同樣的道理。

function getUserInfo(userName, age) {}

getUserInfo("kalan", 20)

// getUserInfo(userName, age)
// 					  [string]  [number]
getUserInfo("kalan", 20)

如果有相同的函式呼叫的話,可以讓參數對齊方便閱讀。

command = {
	{ "timeout"      ,   null,     cmd_spec_timeout},
	{ "timestamping" ,   bull,     cmd_adj_boolean},
f
}

在寫註解的時候,其實不需要太拘泥,把自己當時想到的想法,以及這個 function 應該做的事情寫下來就好了,有時候過了一段時間你會忘記這個 function 在幹麻。

--

2. 註解篇:

註解是為了讓其他人了解程式設計者的想法而存在的。同時也讓自己了解自己當初的想法。

  • 如何撰寫好的註解,以及哪些東西不需要註解
  • 不該註解的部分
  • 為讀者設身處地著想

不要讓註解搶走了程式該有的位置

避免寫作抗拒。這通常需要一段時間才能體會到。但當專案架構還沒有變得複雜之前就盡快寫註解絕對是件好事。不然你只能寫下違背良心的

// TODO: refactor

然後就再也沒有下文了。

結語

  • 選用特定寫法的原因
  • 程式碼中的缺陷。
  • 使用者會對哪個部分感到疑惑

維持註解簡潔

  • 用更精確的方式來描述自己的程式碼,並且不要用代名詞來描述參數。
  • 如果參數的行為較複雜,可以直接給範例讓使用者一目了然。

3. 流程控制:

最常見的就是 if/else 的判斷。書中提供一個準則,就是肯定的條件句先擺前面、先處理簡單的狀況。 如果你的 function / method 是有返回值的,就盡快讓他 return 吧!

  • 善用迪摩根定律:這個定律應該理工科都有印象吧!他可以把一些比較複雜的邏輯判斷簡化。

與複雜的邏輯搏鬥:

書中提到一個蠻有趣的方式,我想把它記錄下來。 在實作 range 的時候,我們可能有一個方法 overlapWith ,來判斷兩個 range 之間是否有重疊。比起使用直接比對這兩個 range 是否有重疊,不如比對這兩個 range 是否不重疊更簡單。因為只要比對兩種狀況 => other 的 end 在 range 之前。 other 的 start 在 end 之後。

將龐大的表示式用變數裝起來。

$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")

// refactor

const $thumbUp = $(".thumb_up")
const highLight = "highlighted"

$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")
$(".thumb_up").removeClass("highlighted")

//

4. 變數:

變數存在越久,就越難 debug

  • 減少不必要的變數宣告。

什麼是不必要的變數?

  1. 無法讓意思變得更言簡意賅

  2. 本身的邏輯不複雜,不需要再用變數取代

  3. 只使用一次

  4. 使用一次性寫入的變數

在 functional programming 當中,我們希望 function 是 pure 且 immutable 的,對於變數來說也是,盡量讓你的變數為 const 或 immutable 的。這樣子不僅你對 function 可以一目了然,也比較容易掌握出錯的點在哪裡。

將想法轉為程式碼:

先用口語敘述行為,再把程式的行為轉換為程式碼。這樣子可以幫助程式設計師寫出更自然的程式碼。

避免撰寫不必要的程式碼

  • 了解需求
  • 重新思考需求
  • 定期閱讀 API 以維持對標準函式庫的熟悉度