# B0001 易讀程式碼的特性:白話 ## 講白話吧! 一般而言,程式碼是否易讀沒有客觀標準定義,好比人們在判斷一篇文章是平易近人還是艱澀難懂,也沒有統一的客觀方式去決定。即使如此,我們還是可以依據是否滿足某些**特性**來大致判斷程式碼是否相對易讀。這些特性中,**「白話」** 是一個很重要的指標。 下面例子可以說明**白話**的程式碼是什麼概念。 ## 極度難以理解的程式碼 請先嘗試閱讀並理解下面這段程式碼。 ```c++= int getLight_1() { static int a = 0; static int b = 0; static int c = 0; if(a < 4) { ++a; return 1; } else if (b < 1) { ++b; return 2; } else if (c < 5) { ++c; return 3; } else { a = 1; b = 0; c = 0; return 1; } } ``` 這些程式碼每一行單獨看都是不難懂的,但是全部兜再一起就不知道想要做什麼事情。理解不了請不需要覺得挫折,因為 `getLight_1` 是幾乎無法被直接理解的。就算給了一個提示:**這是一個紅綠燈控制程式碼**,之後再回頭閱讀一次程式碼,也不一定能讀懂具體行為。 ## 稍微可以理解的程式碼 如果不要偷懶,多敲幾個字,不要用難以解讀的`a, b, c`作為變數名稱的話,就可以得到 `getLight_2` 這樣的程式碼。 ```c++= int getLight_2() { static int counter_green = 0; static int counter_yellow = 0; static int counter_red = 0; if (counter_green < 4) { ++counter_green; return 1; } else if (counter_yellow < 1) { ++counter_yellow; return 2; } else if (counter_red < 5) { ++counter_red; return 3; } else { counter_green = 1; counter_yellow = 0; counter_red = 0; return 1; } } ``` `getLight_2` 算是稍微可以理解了。從這個程式碼可以看到對於綠燈、黃燈、紅燈各有一個 counter,並透過 counter 值決定返回值。不過`getLight_2` 的好理解也僅止於認知到 counter 本身,至於返回值的意義與 counter 的判斷規則,還無法輕易理解。 ## 更好理解的程式碼 為清楚明白返回值的意義與 counter 的判斷規則,只要將難以解釋的純數字用文字替換,就能更好理解。 `getLight_3` 提供了替換後的程式碼。 ```c++= enum LightId { light_green = 1, light_yellow = 2, light_red = 3 }; LightId getLight_3() { const int duration_green = 4; const int duration_yellow = 1; const int duration_red = 5; static int counter_green = 0; static int counter_yellow = 0; static int counter_red = 0; if (counter_green < duration_green) { ++counter_green; return light_green; } else if (counter_yellow < duration_yellow) { ++counter_yellow; return light_yellow; } else if (counter_red < duration_red) { ++counter_red; return light_red; } else { counter_green = 1; counter_yellow = 0; counter_red = 0; return light_green; } } ``` 這份程式碼首先將返回值用 `enum` 代替純數字,以清楚表達返回值的意義;接著將 counter 判斷式的對象以 duration 來命名,就能明白 counter 是用來計數該燈號的區間。 ## 翻譯機 其實3段程式碼差異沒有很大,基本架構都相同,只是單純換換名字而已,就能讓**易讀性**有顯著的提升。提升的關鍵,在於程式碼是否 **「白話」** 。所謂白話,就是閱讀起來要能直接得到字面的意思,而不是要帶著**翻譯機**才能讀懂。 這3段程式碼可以看到3個翻譯機: ### counter 翻譯機 `getLight_1` 到 `getLight_2` 需翻譯出 counter 。 * `a` 就是 `counter_green` * `b` 就是 `counter_yellow` * `c` 就是 `counter_red` ### 燈號翻譯機 `getLight_2` 到 `getLight_3` 需翻譯出返回值所代表的燈號 。 * 返回`1` 就是綠燈 * 返回`2` 就是黃燈 * 返回`3` 就是紅燈 ### 燈號時長翻譯機 `getLight_2` 到 `getLight_3` 需翻譯出每個燈號所維持的秒數。 * `counter_green` 判斷的 `4` 表示是綠燈維持4秒 * `counter_yellow` 判斷的 `1` 表示是黃燈維持1秒 * `counter_red` 判斷的 `5` 表示是紅燈維持5秒 ## 文言文當然難懂 當我們要嘗試理解 `getLight_1`,卻需要帶著3個翻譯機才能讀懂。有沒有覺得這種體驗似曾相識?對,就是閱讀文言文。當我們在閱讀文言文的時候,許多詞句都需要再翻譯過,才能符合平常習慣的用法、才能符合**白話文**的理解方式。需要帶著翻譯機閱讀的程式碼就像文言文,考驗著讀者的翻譯能力。 ## 挑戰記憶力當然不易讀 需要翻譯機的程式碼,不論是對程式碼的讀者或作者,都是在**挑戰記憶力**。怎麼說呢?因為每個翻譯機都是特定一段、或者數段的程式碼所專有的,不同程式碼之間翻譯機沒有共通性。 比方說燈號翻譯機,1、2、3分別是綠燈、黃燈、紅燈的這件事情,不是一個普遍常識,在其他跟紅綠燈有關的程式碼並不存在這個關聯,也不存在跟紅綠燈無關的程式碼中,它只發生在這個案例。因此我們不能累積這個經驗到其他的程式碼上,也不能將其他程式碼的翻譯機對照到這個紅綠燈案例上。 既然不能累積經驗、讓經驗變成常識,只有強記一途。熟記每一個翻譯機,在閱讀或撰寫程式碼的時候,隨時用翻譯機做對照。別看這個案例也才只有3個翻譯機,或許不難記憶;如果不好好控制,翻譯機的需求量可能要十幾個、甚至數十個,就演變成**挑戰記憶力**了。既然直接寫出翻譯後的文字(也就是白話)就不用記了,又何必對人腦如此不友善呢? ## 白話不會寫起來比較久 還記得 [A0003](/bZBvxIR_QW2BrLPcfWQO3Q) 提過**慢慢來比較快**的概念嗎? `getLight_3` 很明顯寫起來不會比 `getLight_1` 慢,而且可以想像的是,撰寫 `getLight_1` 這樣程式碼的過程必須不斷查找翻譯機,萬一查錯翻譯機又要花很多時間 Debug 檢查正確性;而 `getLight_3` 相對白話了許多,除了不小心打錯字以外,應該很難出錯要 Debug 了吧。說穿了,照顧到**白話**的程式碼通常能更快完成,而**不寫白話**只是**自以為程式碼會比較快完成**,給自己懶得思考的藉口罷了! --- ## 延伸閱讀 [C0001 不白話的程式碼:文字加密](/tdzMvnrYTrySrC5iokmOtQ)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up