返回總目錄 本小節目錄 Replace Data Value with Object(以對象取代數據值) Change Value to Reference(將值對象改為引用對象) Change Reference to Value(將引用對象改為值對象) 1Replace Data Value w ...
本小節目錄
- Replace Data Value with Object(以對象取代數據值)
- Change Value to Reference(將值對象改為引用對象)
- Change Reference to Value(將引用對象改為值對象)
1Replace Data Value with Object(以對象取代數據值)
概要
你有一個數據項,需要與其他數據和行為一起使用才有意義。將數據項變成對象。
動機
開發初期,你往往決定以簡單的數據項表示簡單的情況。但是,隨著開發的進行,你可能會發現,這些簡單數據項不再那麼簡單了。如果這樣的數據項只有一兩個,你還可以把相關函數放進數據項所屬的對象里;但是Duplicated Code壞味道和 Feature Envy壞味道很快就會從代碼中散髮出來,當這些壞味道開始出現,就應該將數據值變成對象。
範例
下麵有個代表“定單”的Order類,其中一個字元串記錄定單客戶。現在,希望改用一個對象來表示客戶信息,這樣就有充裕的彈性保存客戶地址。信用等級等信息,也得以安置這些信息的操作行為。
class Order { public string Customer { get; } public Order(string customer) { Customer = customer; } public static int NumberOfOrderFor(List<Order> orders, string customer) { return orders.Count(order => order.Customer == customer); } }
首先新建一個Customer類來表示“客戶”概念。
class Customer { public string Name { get; } public Customer(string name) { Name = name; } }
現在來重構Order類:
class Order { public Customer Customer { get; set; } public Order(string customerName) { Customer = new Customer(customerName); } public static int NumberOfOrderFor(List<Order> orders, string customerName) { return orders.Count(order => order.Customer.Name == customerName); } }
小結
以對象取代數據值可以減少重覆代碼。
2Change Value to Reference(將值對象改為引用對象)
概要
你從一個類衍生出許多彼此相等的實例,希望將它們替換為同一個對象。將這個值對象變成引用對象。
動機
在許多系統中,都可以對對象分類:引用對象和值對象。前者就像“客戶”、“帳戶”這樣的東西,每個對象都代表真實世界中的一個實物,你可以直接以相等操作符(==,用來檢驗同一性)檢查兩個對象是否相等。後者則是像“日期”、“錢”這樣的東西,它們完全由其所含的數據值來定義,你並不在意副本的存在;系統中或許存在成百上千個內容為“1/1/2000”的“日期”對象。當然,你也需要知道兩個值對象是否相等,所以你需要覆寫Equals()以及GetHashCode())。
關於值對象和引用對象的更多介紹,請參考一下鏈接:
http://blog.csdn.net/u011453312/article/details/27269341
http://blog.jobbole.com/99723/
要在引用對象和值對象之間做選擇有時並不容易。有時侯,你會從一個簡單的值對象開始,在其中保存少量不可修改的數據。而後,你可能會希望給這個對象加入一些可修改數據,並確保對任何一個對象的修改都能影響到所有引用此一對象的地方。這時候你就需要將這個對象變成一個引用對象。
範例
看上一節重構後的代碼:
class Customer { public string Name { get; } public Customer(string name) { Name = name; } } class Order { public Customer Customer { get; set; } public Order(string customerName) { Customer = new Customer(customerName); } public static int NumberOfOrderFor(List<Order> orders, string customerName) { return orders.Count(order => order.Customer.Name == customerName); } }
到目前為止,Customer對象還是值對象。就算多份定單屬於同一客戶,但每個order對象還是擁有各自的Customer對象。我希望改變這一現狀,使得一旦同一客戶擁有多份不同定單,代表這些定單的所有Order對象就可以共用同一個Customer對象。
首先使用Replace Constructor with Factory Method,在Customer類中定義一個工廠函數:
public static Customer Create(string name) { return new Customer(name); }
然後把原本調用構造函數的地方改為調用工廠函數:
class Order { public Customer Customer { get; set; } public Order(string customerName) { Customer = Customer.Create(customerName); } ... }
再把構造函數改為私有的:
private Customer(string name) { Name = name; }
接著在Customer類中聲明一個字典集合,把它保存在Customer類的static欄位中,讓Customer類作為訪問點:
private static Dictionary<string, Customer> _instance = new Dictionary<string, Customer>();
然後在接到請求時事先將Customer創建好。
public static void LoadCustomers() { new Customer("Lemon Car Hire").Store(); new Customer("Associate Coffee MAchines").Store(); new Customer("Bilson Gasworks").Store(); } private void Store() { _instance.Add(Name, this); }
最後,修改工廠函數,讓他返回預先創建好的Customer對象,並使用Rename Method修改該函數的名稱:
public static Customer GetNamed(string name) { _instance.TryGetValue(name, out Customer customer); return customer; }
3Change Reference to Value(將引用對象改為值對象)
概要
你有一個引用對象,很小且不可變,而且不易管理。將它變為一個值對象。
動機
如果你發現引用對象開始變得難以使用,你就考慮是否應該把它改為值對象。引用對象必須被某種方式控制,你總是必須向其控制者請求適當的引用對象。它們可能造成記憶體區域之間錯綜複雜的關聯。在分佈系統和併發系統中,不可變的值對象特別有用,因為你無需考慮它們的同步關係。
值對象有一個非常重要的特性----它應該是不可變的。無論何時,只要你調用同一個對象的同一個查詢函數,它返回的結果應該是一樣的。如果你可以保證這一點,你可以放心的讓多個對象表示同一個事物。如果值對象是可變的,你就必須確保某一個對象的修改會自動更新到其他代表相同事物的對象中去。那此時你應該使用引用對象了。
範例
class Currency { public string Code { get; } private Currency(string code) { Code = code; } }
這個貨幣類目前是一個引用對象。要把一個引用對象變成值對象,關鍵動作是觀察它是否不可變,如果不是,那就別用此次重構,因為後期的別名同步問題還很煩人。
在這裡Currency是不可變的,所以重寫Equals()和GetHashCode():
public override bool Equals(object obj) { var currency = obj as Currency; return currency != null && Code == currency.Code; } public override int GetHashCode() { return Code.GetHashCode(); }
完成這個之後我們進行編譯,測試。現在我們想創建多少個Currency都沒問題,我們還可以把他的構造函數聲明為public,我們可以直接用構造函數來獲取Currency實例,從而去掉工廠函數和控制實例創建的行為。
小結
我們一直說值對象是不可變的。那麼什麼是不可變?不可變意味著如果你以Money來表示金錢,那麼Money通常是一個不可變的值。這並不是意味著你的薪資不能改變。而是意味著如果你改變薪資,你必須用另一個對象來替換現在的Money對象,而不是在現在的Money對象上做修改。你和Money對象之間的關係誒改變,但Money對象自身不能改變。
To Be Continued……