Rider 最具威力的重構功能之一

在實務上常會發現一些 parameter 總是以 primitive type 一起出現,導致 method 內的 parameter 個數過多,也就是 Code Smell 所謂的 Primitive Obsession,解決方式是將這些 parameter 抽成 class,這在 Refactoring 稱為 Introduce Parameter Object,而在 Rider 則稱為 Transform Parameters,且實務上常常搭配 Make Method Non-Static 一起重構。

Version


macOS High Sierra 10.13.4
.NET Core 2.1
Rider 2018.1.3

重構前


StateroomService.cs

tp004

第 9 行

1
public decimal TotalAmount(DateTime startTime, DateTime endTime)

我們發現 startTimeendTime 總是一起出現,這正是典型的 Primitive Obsession,因此我們想將 startTimeendTime 抽成 Period class。

Primitive Obsession

過度使用程式語言所提供的基本型別,卻忘記使用使用更有意義的 class 型別

Transform Parameters


將 primitive type 的 parameter 抽成 class,也就是 Refactoring 中的 Introduce Parameter Object

tp000

  1. 將 cursor 放到要抽的 parameter 上
  2. 按熱鍵 ⌃ + T ,選擇 Transform Parameters

tp001

  1. 選擇要抽出的 parameter
  2. Method receives : 選擇要抽成 Class 或 Tuple
  3. 輸入 class 名稱
  4. Next 繼續

tp002

  1. 抽出了 Period class
  2. TotalAmount 的 parameter 也由兩個 DateTime 重構成只有一個 Period

Rider 預設會將 class 抽在同一個檔案,但實務上建議 1 個檔案只有 1 個 class,因此會繼續重構,將 Period class 獨立成一個檔案

Move Class to File


將 class 抽成獨立的檔案。

tp005

  1. 將 cursor 放到 Period
  2. 按熱鍵 ⌥ + ↩,選擇 Move to 'Period.cs'

tp006

  1. 重構出獨立的 Period.cs 檔案

Extract Method


將一段 code 抽成 method。

tp007

  1. daysperiod.EndTimeperiod.StartTime 計算所得,明明在 StateroomService 內,卻一直讀取 Period 的資料,這正是典型的 Feature Envy,需進一步抽成 method,然後 Move MethodPeriod

Feature Envy

不斷的向其他 class 取值,然後在自己的 class 運算

tp008

  1. 選擇要抽取的 code
  2. 按熱鍵 ⌃ + T ,選擇 Extract Method

tp009

  1. 選擇 Method
  2. Next 繼續

tp010

  1. 抽出 Days method
  2. 預設將 Make static 打勾
  3. Next 繼續

Make static 的重要性

  • 若能抽成 static,Rider 會自動將 Make static 打勾,這表示該 method 已經與此 class 沒有任何瓜葛 (沒使用 field),可隨時 Move Method 到其他 class
  • 若不能抽成 static,則表示此 method 仍使用該 class 的 field,須先進一步重構,直到能抽成 static method 後,才能 Move Method 到其他 class
  • 若要繼續使用 Make Method Non-Static 重構,則 static method 為其必要條件,否則無法繼續使用 Make Method Non-Static

Make Method Non-Static


Static method 重構到 parameter object 的 instance method,也就是 Refactoring 中的 Move Method

tp011

  1. 將 cursor 放在 Days()
  2. 按熱鍵 ⌃ + T ,選擇 Make Method Non-Static

tp012

  1. 選擇要 Move MethodPeriod
  2. Next 繼續

tp013

  1. Days() 被搬到 Period

tp014

  1. 直接呼叫 period.Days()

Make Method Non-Static 本質上就是 Move Method,只是因為 Make Method Non-Static 的名稱,會讓人誤以為只是單純的將一個 method 由 static 重構成 non-static 而已,但事實上不只單純的重構成 non-static,還會將整個 method 搬到 parameter object 上,所以若 Extract Method 所抽出的 parameter,不是要我們要 Move Method 的 class,我們還會透過 Change Parameter 加以重構 parameter,讓 Make Method Non-Static 可以繼續進行

Inline Variable


將不必要的 中介變數 去除。

tp015

  1. days 可以再加以 inline

tp016

  1. 將 cursor 放在 days
  2. 按熱鍵 ⌃ + T ,選擇 Inline Variable

tp017

  1. days 變數被 inline 掉了

tp018

  1. 同理,days 也可再被 inline 掉

tp019

  1. days 變數被 inline 掉了

由於 職責 變清楚,因此少掉了很多不必要的 parameter 與 variable,不只 code 變的清爽,可讀性也高,也更容易維護

執行單元測試


tp020

  • 重新跑單元測試,確認 production code 沒被重構壞掉

Conclusion


  • Primitive Obsession 是很常見的 Code Smell,由於太堅持使用 primitive type,而喪失發現 object 的機會,導致 OOP 名存實亡,透過 Rider 的 Transform Parameters,我們可以直接將 parameter 將抽成 class,這對 legacy code 重構非常方便
  • Feature Envy 也是很常見的 Code Smell,由於 class A 一直使用 class B,因此需要很多中介的 parameter 與 variable,透過 Rider 的 Extract Method,我們可以直接將 class A 的一段 code 抽成 method,然後透過 Make Method Non-Static 般到 class B,這會使得 class 與 class 間的 職責 更為清楚,也可將不必要的 parameter 與 variable 重構掉
  • Transform ParametersMake Method Non-Static 常常一起執行,因為既然已經使用 Transform Parameters 將 parameter 抽成新的 object,根據 OOP 的 職責 思維,必然將一些 code 透過 Make Method Non-Static 搬到新的 class,如此才能避免 Primitive ObsessionFeature Envy

Sample Code


完整的範例可以在我的 GitHub 上找到

2018-07-08