簡述
如果方法有超過3個以上的參數,調用方法時就會顯得冗詞贅句。這時將多個參數封裝成一個對象,調用方法會顯得干凈整潔。
這就是本文要講的重構策略“引入參數對象”——將方法的參數封為類,並用這個類的對象替換方法中原有的參數。
引入參數對象
下圖演示了這個重構策略,OrderSerivce表示訂單服務,GetOrders()方法根據一些條件獲取訂單信息。
在重構前,GetOrders()方法看起來像長篇大論,調用時也頗為麻煩;在重構后,GetOrders()方法看起來言簡意賅,調用時僅需要傳入一個參數。
優點
這個策略在我看來至少有兩個優點:
1. 可以減少方法的參數個數,調用方法時會更加干凈整潔。
2. 當方法需要追加或者刪除參數時,不需要修改方法簽名。
注意點
1. 意味着你要在項目中追加新的class。
2. 並非死板地將所有的參數都放到一個參數對象中,需要確定這些參數屬於某一個“共同的概念”。
3. 方法調用參數減少了,給“參數對象”起個好名字也很重要。
4. 項目開發階段,如果方法的參數隨時可能變更,建議直接將參數設計為對象參數。
例如:在一個平面坐標系統中,基於坐標和半徑畫圓。
public void CreateCircle(double x, double y, double radius) { // do work }
如果使用參數對象,我們可以用兩種方式來表達。
方式1:將坐標封裝為參數對象
方式2:將坐標 + 半徑封裝為參數對象
使用合適的參數對象命名可以將這兩種方式區分開來。
方式1,提煉出“坐標”概念,於是就有了Point對象;方式2,提煉出“圓”概念,故使用了Circle對象作為參數。
public class ShapeTool { // 方式1:封裝坐標 public void CreateCircle(Point point, double radius) { // do work } // 方式1:封裝坐標 + 半徑 public void CreateCircle(Circle circle) { // do work } } public class Point { public double X { get; set; } public double Y { get; set; } } public class Circle { public Point Point { get; set; } public double Radius { get; set; } }
示例
重構前
這段代碼用於“學生注冊課程”。
public class Registration { public void Create(decimal amount, Student student, IEnumerable<Course> courses, decimal credits) { // do work } } public class Student { } public class Course { }
Create()方法的看起來難以理解,它的參數個數很多,並且沒有良好的分類,我們應將這些參數封裝起來。
重構后
針對“學生注冊課程”這個場景,我們提煉出一個概念——”注冊上下文“,這個上下文可以包含以上方法的所有參數。
重構后,Create()方法看起來簡潔易懂,"RegistrationContext"這個命名恰到好處,所有參數都放到RegistrationContext類里了。
public class RegistrationContext { public decimal Amount { get; set; } public Student Student { get; set; } public IEnumerable<Course> Courses { get; set; } public decimal Credits { get; set; } } public class Registration { public void Create(RegistrationContext registrationContext) { // do work } } public class Student { } public class Course { }
在一些應用場景中,當你的方法擁有很多參數,且這些參數表示不同的意義時,我們很難通過這些參數的名稱提煉出一個合適的名詞。
例如:為了將HttpRequest和HttpResponse放到一個對象中,若將這個對象命名為"HttpRequestAndResponse"會顯得喋喋不休。
這時,我們可以用一種偷懶的做法,把這樣的對象命名為“xxx上下文”。
例如,在ASP.NET中HttpContext和ViewContext就是這樣的對象。