重構 - 改善既有程式的設計 - Large Method
April 09, 2020
這篇文章討論《重構 - 改善既有程式的設計》裡的3.2 - Large Method
圖片以及程式碼來源自重構 - 改善既有程式的設計
過長函式
一個函式不應該太長 一般來說超過十行就很值得考慮重構
起因
寫程式比讀程式簡單得多 所以通常一有新增需求 人們就直接把程式碼加在原本的函式裡
所以函式越變越大 味道越變越怪
解法
你應該更積極的去分解函式 有一個原則可以遵循:
當你感覺需要註釋來說明什麼的時候 你就把這區塊拉近另一個函式 並以用途(非實作方法)來命名新函式 即使只是一行的程式 只要函式名稱可以清楚的說明用途 就該這麼做
要不要分離出新的函式的判斷基準 不在於函式的長度夠不夠長 而是這個函式 做什麼 和 如何做的semantic distance(語意距離)
“做什麼” 是比 “如何做” 還要高一層的抽象 所以如果這兩者的語意距離很遠 那就應該分離出個新的函式
好 開始聊常見的解法
Decompose Conditional 分解條件式
把你複雜的if-else 條件判斷分離成小函式
變成這樣
當你需要特別寫註解 就很值得分離出來 把註解的內容當作函式名稱
但你仔細看會看到 當你需要Extract Method的時候 常常需要把local variable(amount)一起傳進那個新的函式 這樣子可讀性可能也很難提升
所以我們可以用Replace Temp with Query, Introduce Parameter Object 和 Preserve Whole Object來讓程式簡潔一點
Replace Temp with Query
原本你需要一個區域變數
現在你用一個函式取代
為什麼要這樣呢 因為暫時性的區域變數就只是出現一下 如果一個類別裡有很多地方要用 會導致你的類別有很多很長的函式 這麼做之後 類別裡的所有人都可以直接拿到這個值
所以在我們Extract Method的例子裡 我們就不用把amount
傳來傳去 我們就可以在printDetails()
裡呼叫getAmount()
Introduce Parameter Object
如果參數間的關係很強 可以把參數們包成一個參數物件
Preserve Whole Object
如果你把一個物件裡的若干資料拿出來傳給別人
那你不如整個物件傳進去
好處是參數列更穩定 如果你想要多傳一個物件裡的參數 你公開的interface甚至不用改
壞處就是增加了兩個函式之間的dependency
大招: Replace Method with Method Object
如果以上的方式都不行 代表說你有太多的區域變數需要傳來傳去 讓你很難Extract Method 也可能是你Extract Method之後程式碼的好讀性也沒上升
大招就是 把這個函式放進一個單獨物件之中 這樣的話每個區域變數就變成物件之內的欄位(fields) 那之後你愛怎麼用都行
來個例子
因為區域變數太多 傳來傳去很醜 我們就直接多加一個類別PriceCalculator
有梗吧