重構
這系列是專門記錄在物件中進行物件中的搬移手法。
一、Move Method
用途:將一個類別某方法、搬移到另一個按照直則劃分該有方法的類別中。
//前
// 遺留的 Person 類別
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
//Step1 Ctrl+Shift+R 選擇 Move Instance Move
public string GetPersonDetails(PersonManager personManager)
{
return $"Name: {Name}, Age: {Age}";
}
}
// 遺留的 PersonManager 類別
public class PersonManager
{
}
//後
// 遺留的 Person 類別
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
// 遺留的 PersonManager 類別
public class PersonManager
{
public string GetPersonDetails(Person person)
{
return $"Name: {person.Name}, Age: {person.Age}";
}
}
二、Move Field
用途:將某一段的屬性移動到另一個合適的類別中。
//前
public class Employee
{
}
public class Salary
{
public string employeeId { get; set; }
public string employeeName { get; set; }
}
//後
public class Employee
{
public string employeeId { get; set; }
public string employeeName { get; set; }
}
public class Salary
{
}
三、Extrace Class
用途:將一個類別某個字段或方法來抽取出來放到新的類別中,以提升系統的功能內聚。
public class OrderService
{
public OrderService()
{
}
//Step1:該方法 Ctrl+Shift+R 選取Extract Class
public bool IsExistCustomer(string customerId)
{
// 判斷客戶是否存在
return true;
}
public void CreateOrder(Customer customer)
{
// 建立訂單
}
}
//後
public class customerService
{
public customerService()
{
}
public bool IsExistCustomer(string customerId)
{
// 判斷客戶是否存在
return true;
}
}
public class OrderService
{
private readonly customerService _customerService;
public OrderService()
{
_customerService = new customerService();
}
public void CreateOrder(Customer customer)
{
// 建立訂單
}
}
四、InLine Class
用途:當如果A類別是B類別一個屬性,將A類別刪除,並將A類別內容歸類到B類中(Extrace Class反向操作)。
//前
public class customerService
{
public customerService()
{
}
public bool IsExistCustomer(string customerId)
{
// 判斷客戶是否存在
return true;
}
}
public class OrderService
{
// 在_customerService 執行Step1:Ctrl+Shift+R 選取InLine Class
private readonly customerService _customerService;
public OrderService()
{
_customerService = new customerService();
}
//Step1:Ctrl+Shift+R 選取Extract Class
public void CreateOrder(Customer customer)
{
// 建立訂單
}
}
//後
public class OrderService
{
public OrderService()
{
}
public void CreateOrder(Customer customer)
{
// 建立訂單
}
public bool IsExistCustomer(string customerId)
{
// 判斷客戶是否存在
return true;
}
}
五、Hide Delegate
用途:當Clinet程式要調用A類別也要調用B類別,而A和B又是潛在委託關係,如A聚合B,讓Clinet程式不要直接呼叫A類別,透過B類別,訪問A類別。
//前
public class Person
{
private Department _department;
public Department GetDepartment()
{
return _department;
}
public void SetDepartment(Department value)
{
_department = value;
}
// 使用 Hide Delegate,透過 Department 的方法存取相關屬性
//public Person GetManager()
//{
// return _department.GetManager();
//}
}
public class Department
{
private string _chargeCode;
private Person _manager;
public Department(Person person)
{
_manager = person;
}
public Person GetManager()
{
return _manager;
}
public string ChargeCode
{
get { return _chargeCode; }
set { _chargeCode = value; }
}
}
class Client
{
public void Main()
{
var john = new Person();
//原本
var manager = john.GetDepartment().GetManager();
//透過Hide Delegate 改呼叫Person的方法
//var manager1 = john.GetManager();
}
}
//後
public class Person
{
private Department _department;
// 使用 Hide Delegate,透過 Department 的方法存取相關屬性
public Person GetManager()
{
return _department.GetManager();
}
}
public class Department
{
private string _chargeCode;
private Person _manager;
public Department(Person person)
{
_manager = person;
}
public Person GetManager()
{
return _manager;
}
public string ChargeCode
{
get { return _chargeCode; }
set { _chargeCode = value; }
}
}
class Client
{
public void Main()
{
var john = new Person();
//原本
//var manager = john.GetDepartment().GetManager();
//透過Hide Delegate 改呼叫Person的方法
var manager1 = john.GetManager();
}
}
六、Remove Midele Man
用途:如果客戶程序再透過委託A類別調用B類別,則去掉這種委託直接訪問類別B(隱藏委託的反向操作)。
public class Person
{
private Department _department;
public Department GetDepartment()
{
return _department;
}
public void SetDepartment(Department value)
{
_department = value;
}
// 使用 Hide Delegate,透過 Department 的方法存取相關屬性
//public Person GetManager()
//{
// return _department.GetManager();
//}
}
public class Department
{
private string _chargeCode;
private Person _manager;
public Department(Person person)
{
_manager = person;
}
public Person GetManager()
{
return _manager;
}
public string ChargeCode
{
get { return _chargeCode; }
set { _chargeCode = value; }
}
}
class Client
{
public void Main()
{
var john = new Person();
//原本
var manager = john.GetDepartment().GetManager();
//透過Hide Delegate 改呼叫Person的方法
//var manager1 = john.GetManager();
}
}
七、Introduce Foreign Method
用途:如果客戶端程式在調用A類別的某方法M時候,需增加一段程式碼,如果大量客戶端程式掉用這方法M時候,都要增加這一段程式碼,應在類別A中增加一個外加方法。
(註記:在大量程式增加這一段程式碼無疑是一種程式的重複,而在M編寫外加方法則實現大量程式碼的重用。)
有一個遺留程式碼的類別是Person類別
//Step1 確認遺留類別
public class Person
{
public DateTime DateOfBirth { get; set; }
}
//Step2 創造方法的新類別
public static class PersonExtensions
{
public static int CalculateAge(this Person person)
{
var today = DateTime.Today;
var age = today.Year - person.DateOfBirth.Year;
if (person.DateOfBirth.Date > today.AddYears(-age))
age--;
return age;
}
}
//Step3 呼叫外部方法
Person person = new Person { DateOfBirth = new DateTime(1988, 1, 1) };
int age = person.CalculateAge();
八、Introduce Local Extension
用途:當系統需要修改一個類別提供某項功能,但你不能修改該類別,為該類別設計一個子類別,並在子類別當中提供這項功能,而讓要使用該功能的客戶端程式調用子類別。
Introduce Foreign Method與Introduce Local Extension區別在於不是簡單增加單獨方法,而是透過新增一個新類別,這個新類別必須繼承原始類別(子類別或包裝原始類別的實例,包裝類)。
- 子類別擴展:在這種方式,必須建立新的類別,繼承原始類別,然後在這個子類別增加你需要新方法或屬性,這種方法原始類別必須允許繼承。
- 包裝類:如果原始類別不予許繼承,可以透過類別透過注入方式來引入內部實例,增加新的方法和屬性。
以Date類別為例
1.子類別擴展
public class ExtendedDate : Date
{
public ExtendedDate(int year, int month, int day) : base(year, month, day) { }
public void AddDays(int days)
{
//自行加入邏輯
}
}
2.包裝類別
public class DateWrapper
{
private Date _date;
public DateWrapper(Date date)
{
_date = date;
}
public void AddDays(int days)
{
}
}
元哥的筆記