iT邦幫忙

2022 iThome 鐵人賽

DAY 6
0

避免副作用

副作用 (Side effects) 就像是謊言。你的函式保證只做一件事,卻暗地裡偷偷做了其他情。

而這種行為,容易造成程式有非預期的錯誤。

試想有「開發者 A」 寫了一個名為 checkPassword 的函式,用處是確認使用者密碼是否正確。於是「共同開發者 B 」在功能有需要時就把這個函式拿去用,結果發生了悲劇。
原來,在 checkPassword 的函式中,在確認密碼之後,會重置、初始化整個使用者資訊,也就是說「開發者 B」預期的功能是單純的 ckeckPassword,然而 A 實際上卻寫了:

const checkPassword = (password) => {
    check(password);
    initUserInfo();
} 

於是在任何有可能需要驗證密碼的地方,工作任務都承擔著被終止或毀掉的風險。
這就是作者所謂的副作用。
而事實上我認為副作用的議題,比較常見的情況是在「深拷貝與淺拷貝」「傳值與傳址」,但受限篇幅且非書中內容,不清楚的人可能就要麻煩再另行爬文。

指令和查詢分離

先來看行程式碼

if(set("username", "unclebob")).....

你能很快速且確切的看出這段程式碼是想做什麼嗎?

是在問「username」有成功設定為「unclebob」嗎?
還是「username」是否在之前就已被設定為「unclebob」?
雖然只有短短一行的程式碼,但它的意圖仍然使人困惑。
也許我們可以將函式重新命名為 setAndCheckExists,但其實這對 if 敘述的可讀性仍然沒有太大的幫助。

真正的解決方式,是將指令 (command) 和查詢 (query) 分開,才能避免這樣模稜兩可的情形。

if(attributeExist("username")){
    setAttribute("username", "unclebob");
    // 略......
}

不要重複自己

在我學程式後進入的第一家公司主管,告訴我的第一個程式原則就是這句話:「Don't Repeat Yourself!」(DRY,不要重複自己原則。)
有些人可能覺得不要重複自己是為了方便、重寫一樣的內容很蠢之類的,但最重要的原因應該是,避免 bug。

程式越多,就越容易出錯,當一段程式碼被重複打了 10 遍後,沒人會反覆檢查裡面到底有沒有打錯。而且,當我們發現這段程式有錯誤時,我們會需要將同樣的修正重複 10 遍,而這麼多次的反覆改動,往往就是醞釀錯誤的溫床。而且將函式重構以避免重複,除了能大大減低撰寫時的低級錯誤 (有多少次我們花了半天除錯,最後發現只是因為一個字母或標點?),也能提升程式可讀性,大大增加程式的品質及可維護性。

如何寫出這樣的程式

寫軟體就如同其他任何的寫作一般,當你寫一篇論文或一篇文章時,你會先直接把想法寫下來,然後開始琢磨,直到讀起來很通順。第一份初稿通常是粗糙而雜亂無章的,所以你開始修改,重新組織整個文章段落,將文章改善置你想要的樣子。

作者說自己也是這樣,一開始是很粗糙的草稿,然後會開始慢慢地修正它,那些隨意的命名、巢狀結構、又臭又長的函式,都會在後面的過程中一一修改,直到符合這三天所分享的函式的準則。但他不會一開始就寫的完美,也不認為有人做得到。


上一篇
函式(二)
下一篇
註解
系列文
重新開始學程式,【無瑕的程式碼:敏捷軟體開發技巧守則】共讀30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言