(179)Change_Value_to_Reference
將實質物件改為引用物件
動機
reference object (引用物件) VS value object (實質物件)
一開始這個物件可能只是少量不可修改的資料,之後又需要加入可以修改的資料
並確保對任一個物件的修改都能影響到所有引用此物件的地方
這時候就會需要reference object
範例
class Customer{
public Customer(string name){
_name = name;
}
public void getName(){
return _name;
}
public const string _name;
}
// client code
``` cs
class Order{
public order(string customerName){
// 重點在這,每次使用Order都會去建立一個Customer
_customer = new Customer(customerName);
}
public void setCustomer(string customernName){
_customer = new Customer(comstomerName);
}
public string getCustomerName(){
return _customer.getName();
}
private Customer _customer;
}
// 還有其他地方會用到Custeomer
private static int numberOfOrdersFor(Collect orders,string customer){
int result = 0;
Iterator iter = orders.iterator();
while (iter.hasNext()){
order each = (Order)iter.next();
if (eash.getCustomerName().equals(suctomer)) result++;
}
return result;
}
情境說明:
這邊所有的Order 都使用到Customer而且是value object
意思是:每個訂單都各自擁有Customer物件,現在有一個bug或是需求,如果客戶相同,必須把所有的Customer都使用同一個物件
希望避免各自擁有Customer又資料不相同。
做法
- 使用(304)Replace_Constructor_With_Factory_Method把創建Customer來控制建立的過程
class Customer{
public static Customer create(string name){
returm new Customer(name);
}
}
// 所以Order呼叫就可以改成
class Order{
public order(string customer){
_customer = Customer.create(customer);
}
}
// 然後把Customer 的建構式改成Private
class Customer{
private Customer(string name){
_name = name;
}
}
// 接著必須決定如何存取Customer物件
// 通常常見的做法是通過一個物件來存取,例如:Order內的欄位,或Customer的欄位
// 以下例子使用Customer
``` cs
private static Dictionary _instances = new Hashtable();
這時候可以決定,要預先或事後載入客戶資料,先用事前載入當範例,反正之後還可以用(139)Substitute_Algorithm
class Custmer{
...
static void loadCustomers(){
new Customer("lemon Car Hire").store();
new Customer("Associated Coffee Machines").store();
new Customer("Bilston Gasworkds").store();
}
private void store(){
_instances.pub(this.getName(),this);
}
}
因為使用預先載入,就需要修改factory method,讓他回傳育先建好的Customer物件
public static Customer create(string name){
return (Customer) _instance.get(name);
}
這邊還可以繼續優化,使用 (273)Raneme_Method表達更清楚的名稱
public static Customer getNamed(string name){
return (Customer) _instance.get(name);
}
後繼
這個例子練習完的核心有二
- 使用factory method來統一建立Customer的過程
- 將建立好的object,統一放在Order或是Customer(此為關鍵,直接使用此物件來當作reference)
以往為了確保Custeomer物件必須相同,我都是直接將Customer直接傳到Function運算之後,再回傳該物件
public Customer somemethof(string name,Customer customer){
// 一些計算
return customer;
}
// 或是使用ref
public void somemethof(string name,ref Customer customer){
// 一些計算
}
但這些方式都沒有這個方法來的直觀,而且如果要修改這些method,也將造成很多困擾
- 使用ref就難以重構(其他引用的地方都要跟著ref,用多了連自己都會亂掉)
- 將物件本身當作參數,表示這method承擔的工作量過大,應該回歸到Customer本身)