天天看點

Replace Data Value with Object(以對象取代資料值)

你有一筆資料項(data item),需要額外的資料和行為。

将這筆資料項變成一個對象。

class Order...

 private string customer;

==>

class Order...

 private Customer _customer;

class Customer...

 private string _name;

動機

一開始你可能會用一個字元串來表示[電話号碼]概念,但是随後你就會發現,電話号碼需要[格式化]、[抽取區号]之類的特殊行為。當這些臭味開始出現,你就應該将資料值(data value)變成對象(object)。

作法

1. 為[待替換數值]建立一個class,在其中聲明一個final值域,其型别和source class中的[待替換數值]型别一樣。然後在新class中加入這個值域的取值函數(getter),再加上一個[接受此值域為參數]的構造函數。

2. 編譯。

3. 将source class中的[待替換數值值域]的型别改為上述的建立class。

4. 修改source class中此一值域的取值函數(getter),令它調用建立class的取值函數。

5. 如果source class構造函數中提及這個[待替換值域](多半是指派動作),我們就修改構造函數,令它改用新class的構造函數來對值域進行指派動作。

6. 修改source class中[待替換值域]的設值函數(setter),令它為新class建立一個實體。

7. 編譯,測試。

8. 現在,你有可能需要對新class使用Change Value to Reference(179)。

下面有一個代表[定單]的Order class,其中以一個字元串記錄定單客戶。現在,我希望改為以一個對象來表示客戶資訊,這樣我就有充裕的彈性儲存客戶位址、信用等級等等資訊,也得以安置這些資訊的操作行為。Order class最初如下:

class Order...

    public Order(String customer) {

       _customer = cusomer;

    }

    public String getCustomer() {

       return _customer;

    }

    public void setCustomer(String arg) {

       _customer = arg;

    }

    private String _customer;

Order class的客戶代碼可能像下面這樣:

private static int numberOfOrdersFor(Collection orders, String customer) {

    int result = 0;

    Iterator iter = orders.iterator();

    while(iter.hasNext()) {

       Order each = (Order)iter.next();

       if(each.getCustomer().equals(customer)) result ++;

    }

    return result;

}

首先,我要建立一個Customer class來表示[客戶]概念。然後在這個class中建立一個final值域,用以儲存一個字元串,這是Order class目前所使用的。我将這個新值域命名為_name,因為這個字元串的用途就是記錄客戶名稱。此外我還要為這個字元串加上取值函數(getter)和構造函數(constructor)。

class Customer {

    public Customer(String name) {

       _name = name;

    }

    public String getName() {

       return _name;

    }

    private final String _name;

}

現在,我要将Order中的_customer值域的型别修改為Customer;并修改所有引用此一值域的函數,讓它們恰當地改而引用Customer實體。其中取值函數和構造函數的修改都很簡單;至于設值函數(setter),我讓它建立一份Customer實體。

class Order...

    public Order(String customer) {

       _customer = new Customer(customer);

    }

    public String getCustomer() {

       return _customer.getName();

    }

    public void setCustomer(String arg) {

       _customer = new Customer(arg);

    }

    private Customer _customer;

設值函數需要建立一份Customer實體,這是因為以前的字元串是個實值對象(value object),是以現在的Customer對象也應該是個實值對象。這也就意味每個Order對象都包含自己的一個Customer對象。注意這樣一條規則:實值對象應該是不可修改内容的--這便可以避免一些讨厭的[别名](aliasing)錯誤。日後或許我會想讓Customer對象成為引用對象(reference object),但那是另一項重構手法的責任。現在我可以編譯并測試了。

public String getCustomerName() {

    return _customer.getName();

}

至于構造函數和設值函數,我就不必修改其簽名(signature)了,但參數名稱得改:

public Order(String customerName) {

    _customer = new Customer(customerName);

}

public void setCustomer(String customerName) {

    _customer = new Customer(customerName);

}

本次 重構到此為止。但是,這個案例和其他很多案例一樣,還需要一個後續步驟。如果想在Customer中加入信用等級、位址之類的其他資訊,現在還做不到,因為目前的Customer還是被作為實值對象(value object)來對待,每個Order對象都擁有自己的Customer對象。為了給Customer class加上信用等級、位址之類的屬性,我必須運用Change Value to Reference(179),這麼一來屬于同一客戶的所有Order對象就可以共享同一個Customer對象。馬上你就可以看到這個例子。