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

將它變成一個value object(實值對象)。

ivaneeo's blog自由的力量,自由的生活。 |
你有一個reference object(引用對象),很小且不可變(immutable),而且不易管理。
將它變成一個value object(實值對象)。 ![]() 在debian中使用sudo必須先添加用戶到sudo中,才可以以root身份執(zhí)行.
范例(Examples)
在Replace Data Value with Object(175)一節(jié)中,我留下了一個重構(gòu)后的程序,本節(jié)范例就從它開始。我們有下列的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對象。我希望改變這一現(xiàn)狀,使得一旦同一客戶擁有多份不同定單,代表這些定單的所有Order對象就可以共享同一個Customer對象。 首先我使用Replace Constructor with Factory Method(304)。這樣,我就可以控制Customer對象的創(chuàng)建過程,這在以后會是非常重要的。我在Customer class中定義這個factory method: class Customer { public static Customer create(String name) { return new Customer(name); } } 然后我把[對構(gòu)造函數(shù)的調(diào)用]替換成[對factory method的調(diào)用]: class Order { public Order(String customer) { _customer = Customer.create(customer); } } 然后我再把構(gòu)造函數(shù)聲明為private: class Customer { private Customer(String name) { _name = name; } } 現(xiàn)在,我必須決定如何訪問Customer對象。我比較喜歡通過另一個對象(例如Order class中的一個值域)來訪問它。但是本例并沒有這樣一個明顯的值域可用于訪問Customer對象。在這種情況下,我通常會創(chuàng)建一個注冊(登錄)對象,作為訪問點。為了簡化我們的例子,我把Customer對象保存在Customer class的一個static值域中,讓Customer class作為訪問點: private static Dictionary _instance = new Hashtable(); 然后我得決定:應該在接到請求時創(chuàng)建新的Customer對象,還是應該預先將它們創(chuàng)建好。這里我選擇后者。在應用程序的啟動代碼(start-up code)中,我先把需要使用的Customer對象加載妥當。這些對象可能來自數(shù)據(jù)庫,也可能來自文件。為求簡單起見,我在代碼中明確生成這些對象。反正以后我總是可以使用Substitute Algorithm(139)來改變它們的創(chuàng)建方式。 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); } 現(xiàn)在,我要修改factory method,讓它返回預先創(chuàng)建好的Customer對象: public static Customer create(String name) { return (Customer)_instance.get(name); } 由于create()總是返回既有的Customer對象,所以我應該使用Rename Method(273)修改這個factory method的名稱,以便強調(diào)(說明)這一點。 class Customer... public static Customer getNamed(String name) { return (Customer)_instances.get(name); }
作法(Mechanics)
動機(Motivation)
在許多系統(tǒng)中,你都可以對對象做一個有用的分類:reference object和value objects。前者就像[客戶]、[帳戶]這樣的東西,每個對象都代表真實世界中的一個實物,你可以直接以相等操作符(==,用來檢驗同一性, identity)檢查兩個對象是否相等。后者則是像[日期]、[錢]這樣的東西,它們完全由其所含的數(shù)據(jù)值來定義,你并不在意副本的存在;系統(tǒng)中或許存 在成百上千個內(nèi)容為“1/1/2000”的[日期]對象。當然,你也需要知道兩個value objects是否相等,所以你需要覆寫equals()(以及hashCode())。 要在reference object和value object之間做選擇有時并不容易。有時侯,你會從一個簡單的value object開始,在其中保存少量不可修改的數(shù)據(jù)。而后,你可能會希望給這個對象加入一些可修改數(shù)據(jù),并確保對任何一個對象的修改都能影響到所有引用此一對象的地方。這時候你就需要將這個對象變成一個reference object。
你有一個class,衍生出許多相等實體(equal instances),你希望將它們替換為單一對象。
將這個value object(實值對象)變成一個reference object(引用對象)。 ![]()
范例(Examples)
下面有一個代表[定單]的Order class,其中以一個字符串記錄定單客戶。現(xiàn)在,我希望改為以一個對象來表示客戶信息,這樣我就有充裕的彈性保存客戶地址、信用等級等等信息,也得以安置這些信息的操作行為。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,因為這個字符串的用途就是記錄客戶名稱。此外我還要為這個字符串加上取值函數(shù)(getter) 和構(gòu)造函數(shù)(constructor)。 class Customer { public Customer(String name) { _name = name; } public String getName() { return _name; } private final String _name; } 現(xiàn)在,我要將Order中的_customer值域的型別修改為Customer;并修改所有引用此一值域的函數(shù),讓它們恰當?shù)馗亩肅ustomer實體。其中取值函數(shù)和構(gòu)造函數(shù)的修改都很簡單;至于設值函數(shù)(setter),我讓它創(chuàng)建一份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; 設值函數(shù)需要創(chuàng)建一份Customer實體,這是因為以前的字符串是個實值對象(value object),所以現(xiàn)在的Customer對象也應該是個實值對象。這也就意味每個Order對象都包含自己的一個Customer對象。注意這樣一條 規(guī)則:實值對象應該是不可修改內(nèi)容的--這便可以避免一些討厭的[別名](aliasing)錯誤。日后或許我會想讓Customer對象成為引用對象(reference object),但那是另一項重構(gòu)手法的責任。現(xiàn)在我可以編譯并測試了。 我需要觀察Order class中的_customer值域的操作函數(shù),并作出一些修改,使它更好地反映出修改后的新形勢。對于取值函數(shù),我會用Rename Method(273)改變其名稱,讓它更清晰地表示,它所返回的是消費者名稱,而不是個Customer對象。 public String getCustomerName() { return _customer.getName(); } 至于構(gòu)造函數(shù)和設值函數(shù),我就不必修改其簽名(signature)了,但參數(shù)名稱得改: public Order(String customerName) { _customer = new Customer(customerName); } public void setCustomer(String customerName) { _customer = new Customer(customerName); } 本次 重構(gòu)到此為止。但是,這個案例和其他很多案例一樣,還需要一個后續(xù)步驟。如果想在Customer中加入信用等級、地址之類的其他信息,現(xiàn)在還做不到,因為目前的Customer還是被作為實值對象(value object)來對待,每個Order對象都擁有自己的Customer對象。為了給Customer class加上信用等級、地址之類的屬性,我必須運用Change Value to Reference(179),這么一來屬于同一客戶的所有Order對象就可以共享同一個Customer對象。馬上你就可以看到這個例子。
作法(Mechanics)
動機(Motivation)
一開始你可能會用一個字符串來表示[電話號碼]概念,但是隨后你就會發(fā)現(xiàn),電話號碼需要[格式化]、[抽取區(qū)號]之類的特殊行為。當這些臭味開始出現(xiàn),你就應該將數(shù)據(jù)值(data value)變成對象(object)。
你有一筆數(shù)據(jù)項(data item),需要額外的數(shù)據(jù)和行為。
將這筆數(shù)據(jù)項變成一個對象。 ![]() |