#
你有一個 reference object(引用對象),很小且不可變(immutable),而且不易管理。
將它變成一個value object (實值對象)。

在debian中使用sudo必須先添加用戶到sudo中,才可以以root身份執行. 修改sudo列表使用visudo命令.
添加內容
任何程序不要用sudo執行不要密碼 你的用戶名 ALL=NOPASSWD:ALL
要密碼的 你的用戶名 ALL=PASSWD:ALL 密碼為你的用戶名密碼非root的
如果限制程序用sudo把all換成相應的程序 只有在普通用戶下用gksu 運行程序時才用root密碼 因為gksu 默任時root用戶
范例(Examples)
在Replace Data Value with Object(175)一節中,我留下了一個重構后的程序,本節范例就從它開始。我們有下列的Customer class:
class Customer {
public Customer(String name) {
_name = name;
}
public String getName() {
return _name;
}
private final String _name;
}
它被以下的order class使用:
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對象:
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對象還是value object。就算多份定單屬于同一客戶,但每個order對象還是擁有各自的Customer對象。我希望改變這一現狀,使得一旦同一客戶擁有多份不同定單,代表這些定單的所有Order對象就可以共享同一個Customer對象。
首先我使用Replace Constructor with Factory Method(304)。這樣,我就可以控制Customer對象的創建過程,這在以后會是非常重要的。我在Customer class中定義這個factory method:
class Customer {
public static Customer create(String name) {
return new Customer(name);
}
}
然后我把[對構造函數的調用]替換成[對factory method的調用]:
class Order {
public Order(String customer) {
_customer = Customer.create(customer);
}
}
然后我再把構造函數聲明為private:
class Customer {
private Customer(String name) {
_name = name;
}
}
現在,我必須決定如何訪問Customer對象。我比較喜歡通過另一個對象(例如Order class中的一個值域)來訪問它。但是本例并沒有這樣一個明顯的值域可用于訪問Customer對象。在這種情況下,我通常會創建一個注冊(登錄)對象,作為訪問點。為了簡化我們的例子,我把Customer對象保存在Customer class的一個static值域中,讓Customer class作為訪問點:
private static Dictionary _instance = new Hashtable();
然后我得決定:應該在接到請求時創建新的Customer對象,還是應該預先將它們創建好。這里我選擇后者。在應用程序的啟動代碼(start-up code)中,我先把需要使用的Customer對象加載妥當。這些對象可能來自數據庫,也可能來自文件。為求簡單起見,我在代碼中明確生成這些對象。反正以后我總是可以使用Substitute Algorithm(139)來改變它們的創建方式。
class Customer...
static void loadCustomers() {
new Customer("Lemon Car Hire").store();
new Customer("Associated Coffee Machines").store();
new Customer("Bilston Gasworks").store();
}
private void store() {
_instance.put(this.getName(), this);
}
現在,我要修改factory method,讓它返回預先創建好的Customer對象:
public static Customer create(String name) {
return (Customer)_instance.get(name);
}
由于create()總是返回既有的Customer對象,所以我應該使用Rename Method(273)修改這個factory method的名稱,以便強調(說明)這一點。
class Customer...
public static Customer getNamed(String name) {
return (Customer)_instances.get(name);
}
作法(Mechanics)
- 使用Replace Constructor with Factor Method(304)。
- 編譯,測試。
- 決定由什么對象負責提供訪問新對象的途徑。
- ==》可能是個靜態字典(static dictionary)或一個注冊對象(registry object)
- ==》你也可以使用多個對象作為新對象的訪問點(access point)。
- 決定這些reference object應該預先創建好,或是應該動態創建。
- ==》如果這些reference object是預先創建好的,而你必須從內存中被它們讀取出來,那么就得確保它們在被需要的時候能夠被及時加載。
- 修改factory method,令它返回reference object。
- ==》如果對象是預先創建好的,你就需要考慮:萬一有人索求一個其實并不存在的對象,要如何處理錯誤?
- ==》你可能希望對factory method使用Rename Method(273),使其傳達這樣的信息;它返回的是一個既存對象。
- 編譯,測試。
動機(Motivation)
在許多系統中,你都可以對對象做一個有用的分類:reference object和value
objects。前者就像[客戶]、[帳戶]這樣的東西,每個對象都代表真實世界中的一個實物,你可以直接以相等操作符(==,用來檢驗同一性,
identity)檢查兩個對象是否相等。后者則是像[日期]、[錢]這樣的東西,它們完全由其所含的數據值來定義,你并不在意副本的存在;系統中或許存
在成百上千個內容為“1/1/2000”的[日期]對象。當然,你也需要知道兩個value objects是否相等,所以你需要覆寫equals()(以及hashCode())。
要在reference object和value object之間做選擇有時并不容易。有時侯,你會從一個簡單的value object開始,在其中保存少量不可修改的數據。而后,你可能會希望給這個對象加入一些可修改數據,并確保對任何一個對象的修改都能影響到所有引用此一對象的地方。這時候你就需要將這個對象變成一個reference object。
你有一個class,衍生出許多相等實體(equal instances),你希望將它們替換為單一對象。
將這個value object(實值對象)變成一個reference object(引用對象)。

范例(Examples)
下面有一個代表[定單]的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),但那是另一項重構手法的責任。現在我可以編譯并測試了。
我需要觀察Order class中的_customer值域的操作函數,并作出一些修改,使它更好地反映出修改后的新形勢。對于取值函數,我會用Rename Method(273)改變其名稱,讓它更清晰地表示,它所返回的是消費者名稱,而不是個Customer對象。
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對象。馬上你就可以看到這個例子。
作法(Mechanics)
- 為[待替換數值]新建一個class,在其中聲明一個final值域,其型別和source class中的[待替換數值]型別一樣。然后在新class中加入這個值域的取值函數(getter),再加上一個[接受此值域為參數]的構造函數。
- 編譯。
- 將source class中的[待替換數值值域]的型別改為上述的新建class。
- 修改source class中此一值域的取值函數(getter),令它調用新建class的取值函數。
- 如果source class構造函數中提及這個[待替換值域](多半是賦值動作),我們就修改構造函數,令它改用新class的構造函數來對值域進行賦值動作。
- 修改source class中[待替換值域]的設值函數(setter),令它為新class創建一個實體。
- 編譯,測試。
- 現在,你有可能需要對新class使用Change Value to Reference(179)。
動機(Motivation)
一開始你可能會用一個字符串來表示[電話號碼]概念,但是隨后你就會發現,電話號碼需要[格式化]、[抽取區號]之類的特殊行為。當這些臭味開始出現,你就應該將數據值(data value)變成對象(object)。
你有一筆數據項(data item),需要額外的數據和行為。
將這筆數據項變成一個對象。

|