# 探索重構 - 2022/01/27 ## 前言 身為一位優秀的 Software Engineer(SWE),就是將自身具備軟體工程知識、商業領域知識應用到軟體開發中,並且產出優良、具有商業價值的軟體。軟體開發的過程中,總是不斷循環著決策、規劃、執行及驗收等週期。 要生產一個好的軟體,從最開始的需求探索、釐清...到實際進入開發環節時的架構規劃、介面設計、細節實作,乃至最後的驗收、驗證等一系列漫長的過程。 SWE 日常主要的工作任務包含:撰寫程式碼(實踐商業需求)、撰寫測試及除錯等;而重構一詞,經常在撰寫程式碼、測試的環節時被聽見。 重構在軟體開發中的意義是什麼?能為軟體帶來什麼更大的價值?今天就要來與大家一起探索何謂重構。 ## 什麼是重構? 簡單來講,在不改變一段程式碼對外部互動、行為的前提下,修改這一段程式碼,便稱為「重構」。 重構不會讓軟體的使用者感受到變化、意外。換句話說,重構不能改變整體功能。  ### 範例:來預測一段程式碼 乍看之下,我們沒辦法立即讀懂 `getResult` 是什麼,以及為什麼要乘 **9.8** 這個數字,反而需要藉由註解才得以勉強預測出該 function 代表的意涵及其意圖。 ```javascript= function getResult(a, b) { // a is mass, b is height, gravity is 9.8 return a * b * 9.8; } function main() { const result = getResult(10, 20); } ``` 例如,在我們看了下面這張圖、或是規格書,加上程式碼中的註解,我們才比較明白、肯定這段程式碼!  然而,經過一段重構後,會讓我們更容易理解這段程式碼的作用--甚至可以不言自明、移除註解。 (註:重構的類型有很多種、包含修改命名、修改底層實作, etc ...。但只要符合上述的定義,皆可稱為重構。) ```javascript= function calculatePotentialEnergy(mass, height) { const gravity = 9.8; return mass * gravity * height; } function main() { const result = calculatePotentialEnergy(10, 20); } ``` 如此我們便得以淺嚐 重構 的好處。但是,這邊有個問題:既然這些程式碼的功能已經完備,又為什麼要回來修改呢?畢竟,重構這個行為大費周章,又看似沒辦法立即獲得好處。 ## 為什麼要重構? 要探討為何需要重構,其實更應該往問題背後的問題去思索。重構能為我們帶來什麼好處?能為開發者、軟體或是企業帶來什麼更大的價值? 畢竟,重構對企業本身就是一個成本,重構將耗費企業的資源(如人力、時間),這些東西都是機會成本。工程師勢必將回答:為何不將人力、時間投入新功能研發或接下更多的新案子等,而是回頭進行重構? ### 從軟體的壽命長度出發: 舉例而言,若某個案子只是給行銷、企劃用一次性活動網頁,只需存在幾天至幾個月;或是某個軟體僅是為了 Demo, 快速進行實驗、市場驗證等。在開發初期,這些專案便暫時不需要考慮重構。畢竟,這些專案的壽命可能很短,時間一到就會消失。 相反地,假如很幸運地,我們短期嘗試的概念,經過驗證後,在市場存活下來;或者我們在開發、維護的產品,本身就是壽命很長的項目。我們或許將開始遭遇困難、痛點。這時候,就可以開始思索重構可能帶來的價值。 ### 現有的痛點: 在當今快速、多變的社會下,既有的軟體將頻繁面臨新功能擴充、需求改變。隨程式碼逐漸增加,軟體複雜度也跟著上升。畢竟,如果我們要修改前人的程式碼,或基於前人的程式碼增添新功能,勢必要對程式碼整體有一定程度的理解。當程式碼行數越來越多,開發者要面對、理解的內容就更多,軟體本身的複雜程度也持續上升。 屆時,軟體的身材,就會從原本的窈窕纖細,不斷地被餵養程式碼增胖,肥胖臃腫的程式碼將造成三項痛點: 1. 難以基於現有基礎(快速)擴充、增添新功能 2. 難以修改、調整現有的功能 3. 難以除錯 因此我們會開始想對軟體進行塑身、雕塑美好身形,僅漂亮地保留需要的部分即可。 基於以上三點,我們可以將重構的目的(為何重構)定調於 1. 讓軟體能更容易被理解,因為好理解即代表好調整、修正;我們在上述位能的例子,已經可以看出這個好處; 2. 改善軟體的設計、架構及消除不必要、重複的程式碼,精練程式碼,因為好的架構使程式碼更容易擴充、修改; 3. 使 Bug 更容易被找出、發現(基於上述兩點,也更容易發現、找出錯誤)。 綜合以上三點,重構是為了能讓工程師更具生產力、能夠更快速地開發程式。 **重構:為了提升程式的開發速度、軟體可擴充程度** ## 何時要重構? 首先,當我們想到要重構時,腦中一定會出現重構的理由。畢竟,進行重構也是一個時間成本,而重構必須保持相同的 input 能獲得一樣的 output。換句話說,重構僅是更改內部實作而已。在這樣的狀況下,老闆也不會希望工程師的產能無故消耗在那,影響產品其他新需求或 Road Map 的推進。 另外,不同類型的重構,有其規模及對應的成本。我們必須根據專案、軟體產品當下的商業階段,以及重構類型,來決定何時該進行重構。 重構的理由百百種,這邊提 2 種: ### 1. 投資報酬率 時間即金錢,此時我們更應該想一下:為什麼我們需要重構?再接著決定如何安排重構計畫。 比如說,重構的理由可能是產品需求改變,或要在既存的程式碼內擴充新功能時,我們發現既存寫法難以擴充、改變,難以適應新需求。現階段可能有更適合的做法,或許可以藉此機會,調整成為更具擴充性的結構。 又或者是,當我們在開發新需求時,不斷受到舊的程式碼阻礙,耽誤了開發時間。這時候,也可以趁此機會思考,能不能調整些許既存的程式碼,讓現在的新功能更好開發。 另個可能性,是技術債的累積與償還。比如近期緊急修復的 Bug,或為了給客戶 Demo 快速製作的 prototype。這些短期、應付特定情境所產出的程式碼,可能會採用一些[臨時解法 (workaround)](https://zh.wikipedia.org/zh-tw/%E6%AC%8A%E8%AE%8A%E6%8E%AA%E6%96%BD)、[寫死](https://zh.wikipedia.org/zh-tw/%E5%AF%AB%E6%AD%BB)特定區域的做法。不過,這些債務應該在特定的時機下償還,以利後續整體性開發。 **想想「為何」此時要重構?「現在」重構的 C/P 值高不高?** 重構 C/P 值是個情境與時機的問題。就如同買股票,每天的價格跟波動也不一樣,為什麼我要這個時機點買入?為何現在要買這麼多?明天的價格可能更好!或是我拿去買其他檔股票更划算等等。 投入重構的時間越多,就像是當下 Buy in 這個功能的單價越高,或許你現在花這麼多時間,進行重構設計是不划算的。舉例來說,我們要特別留意 yagni 原則 (you aren't going to need it);某些我們認定為更彈性的調整,考慮未來更容易擴充的重構,卻有可能是陷入[過度工程](https://www.ithome.com.tw/voice/91845)的浪費。可能在隔天發現市場不需要、或沒有必要調整成這種結構。因此,我們也必須考慮,這個時間拿來做其他事情是否更有價值。 藉由這種詢問自己的過程,我們可以整理思路,根據現況做出適當的決定。 ### 2. 童子軍法則 童子軍法則就是 > 把你使用過後的營地,變得比原本更乾淨。 隨著持續開發的過程,我們更理解相關領域的商業知識,撰寫程式碼的功力也更進步了。當我們再次回到某個既存程式碼區塊時,心中要隨時想著:如何讓程式碼更好。就算只是小規模的調整,例如程式碼順序、修改判斷邏輯或更改變數命名等等,都可以讓整段程式碼的品質提升。我們已經在文章前段的討論,也就是計算位能的範例程式碼中,體現了這項好處。 ## 何時不要重構? ### 1. 路過經過 某些時候,我們會發現某些程式碼片段的設計看似奇特、多此一舉或不合理;然而,我們可能沒想到,那是經過原作者刻意為之或特意避開的。當你不是接手該區域,或正在開發相對應的功能時,請不要隨意修改別人的程式碼;因為,原作者照理應該最熟悉該片段的商業需求、脈絡、與程式執行情境。有時我們路過看到的順手之勞,可能反而破壞了原作的精心設計,反而產生 Bug ;另外,沒有經過溝通的調整,也可能會侵蝕原作的信心。如果真的覺得不妥,或認為需要進行調整時,請先行與原作討論、釐清,以及確認。 ### 2. 沒時間時 我們都會面臨非常時期:需要趕功能上線、緊急修復或 Demo 等等。假設留給我們的時間非常短暫、沒有餘裕時,便不太適合進行重構;因為,這個階段時間寶貴,重構沒辦法快速救火。此時,我們可先全力衝刺、完成交付;否則開發者會分心思考既有龐大程式碼的整體性,及其背後的商業意圖,既無法安心釐清既有程式碼、安排重構計劃,也無法達到快速救火的目標。 此時,建議先將需求完成,待後續恰當時機,再回頭進行調整。 ### 3. 不值得重構時 這點相對比較依賴經驗或限定於某些特定時空下,有些程式碼會被判定為不值得重構。 比如: 如同前述,有些專案的生命週期非常短,一陣子後就再也不會用到。 另外,有些程式碼已不再需要調整或增刪,其存在僅為了維持完整性或整體性。除非有必要去了解該區域是如何運行,不然正常使用沒有障礙時,重構該區域完全無法帶來任何好處。 也有一種可能是:全部「**重寫**」比「重構」還簡單;然而,這也不是個能輕易被做出的判斷。就算開發者相當有經驗,可能也仍須花費相當時間嘗試重構後,才能更清楚評估重構相較於重寫的難易度。 ## 參考資料 [大規模重構 : 奪回源碼庫的控制權](https://www.tenlong.com.tw/products/9789865027780) [重構 : 改善既有代碼的設計](https://www.tenlong.com.tw/products/9789865021832?list_name=rd) [筆記:重構 — Chapter 1 & 2:第一個範例 & 重構原則](https://medium.com/%E5%BE%8C%E7%AB%AF%E6%96%B0%E6%89%8B%E6%9D%91/%E7%AD%86%E8%A8%98-%E9%87%8D%E6%A7%8B-chapter-1-2-%E7%AC%AC%E4%B8%80%E5%80%8B%E7%AF%84%E4%BE%8B-%E9%87%8D%E6%A7%8B%E5%8E%9F%E5%89%87-ca57a6d40f42)
×
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