2022年4月17日 星期日

[重構] - 重構Return , 多個Return整合成單一Return

接手舊專案時,發現該專案有一個現象,

就是單一Function內的Return非常多,

由於是檢測設備的軟體,例如要進行啟動好了,就會有非常多道程序,

只要其中一道程序失敗,便會直接被Return,後面程式碼就不繼續執行了,

檢查是否Return的Code就一直被重複,

一個Function內使用非常多的Return也影響了結構化設計,

因為每次都可能從不同的Return結束該Function

整理以上需求,有兩個需要改善的點:

1.封裝重複出現的Code

2.精簡Return數量

程式碼長得像這樣:

由於重複的程式碼太冗長,因此以註解( /* 進行某些出現錯誤時要做的設定 */ )替代。

簡單說明一下,接手到的Code習慣以回傳0或非0的結果,

0代表成功,非0代表錯誤。

而程式碼原始設計是希望只要有其中一個步驟錯誤了,

就必須直接使用Return跳出,後面的動作都不再進行。


很直覺的,會想把每一次判斷result的if區段包裝成function,

這樣就可以不用一直出現需要好幾行的if區段,

且每一段只需在function參數給予不同的Error Code。

(實務上這一串if區段可能約莫十行)

但問題來了,

由於if區段中包含了return,如果包裝成function,在function內發現需要return時,

回到了上一層,也沒辦法又馬上return呀。


心裡很想改善這種設計,看到一直重複出現的程式碼就非常的不舒服,

一時想不到該如何處理比較好,因為每個檢查點都包含了return敘述,

但檢查點的Code都是相同的,唯一的差異只有Error Code不同,

後來想到Clean Code第57頁,

" 保持單一進入點與單一離開點,Function會更有表達力。"

才想到使用這樣的觀念去整理code,讓code更簡短整潔。

來看看我做了那些修改:


第一點,

我在每一個獨立進行初始化的區塊外都使用 keepGoing 這個flag進行判斷(黃色箭頭處),

如果其中一個流程出現錯誤,那麼 keepGoing 變為false,後面所有的動作都將會略過,

直接到function最後一行的return。

=> 精簡Return數量: 改善架構,一個function只有一進一出。


第二點,

繁雜的if判斷區塊被我包裝成 CanIKeepGoing()。

同時把屬於這個動作流程的Error Code作為參數進行寫入,

閱讀時只需要閱讀一行就能理解該動作流程所屬的Error Code是哪一個,方便理解,

也可以發現在修改後的程式碼中,重複出現的程式碼都不見了(黃色箭頭處)。

=> 封裝重複出現的Code:大幅減少 Code 數量


實務上這樣子的重構有什麼好處?

第一點,

由於所有重複的if判斷區段都被封裝成function,

以上面例子來說,每個if判斷區段原本是十行,都被一行function給取代了。

在實務上我以這個方式只重構專案中的兩個function後,

就讓這兩個function各省下55%的程式碼數量,

兩個function皆是百行以上的內容,

原本需要滑鼠滾輪滾好幾次才看得完的程式碼,

現在只需要滾動一次就看完了,閱讀性提升非常多。


第二點,

如果需要debug,原先需要在每一個if區段內下中斷點(紅色箭頭),

現在只需要在 CanIKeepGoing() 內下中斷點(紅色箭頭),

就可以檢查每一個 initial function的結果了。



第三點,

假設我的 CanIKeepGoing() 內容需要做修正,我只需修改一個地方,

如果照原本的方式去修改,可能要改十幾個使用到if判斷區塊的地方,

非常可能造成沒修改到的漏洞。


第四點,

流程只有一個出口,較容易理解程式脈絡。


你說是不是非常有重構的價值?

沒有留言:

張貼留言

社會新鮮人如何投資?

我的觀點是,在 沒有很多 本錢 的情況下, 別寄望每個月幾千元放到股票或者最近很夯的高股息ETF就能讓你致富, 先投資自己,讓自己的本業收入提高吧。