> 文章列表 > 重构数据-Change Reference to Value将引用对象改为值对象四

重构数据-Change Reference to Value将引用对象改为值对象四

重构数据-Change Reference to Value将引用对象改为值对象四

重构数据-Change Reference to Value将引用对象改为值对象四

1.将引用对象改为值对象

1.1.使用场景

1.值对象和引用对象区别

下面通过客户Customer和订单Order两个对象介绍下它们的区别
值对象:当一个客户Customer下了多个订单Order后,每个订单类都将创建一个客户对象,即使多个订单属于同一个客户,但每个Order对象还是拥有各自的Customer对象。所以对于多个订单Order来说没办法共享同一个客户的信息。

引用对象:同 一客户拥有多份不同定单,代表这些定单的所有Order对象就可以共享同一个Customer对象以及对象中的所有属性信息。

2.可变对象与不可变对象

不可变对象(Immutable Objects)即对象一旦被创建它的状态(对象的数据,也即对象属性值)就不能改变,反之即为可变对象(Mutable Objects)

值对象有一个非常重要的特性:它们应该是不可变的。如果值对象是可变的,你就必须确保对某一对象的修改会自动更新其他“代表相同事物”的对象。这太痛苦了,与其如此还不如把它变成引用对象

上面介绍值对象和引用对象区别 其中值对象就是不可变对象,引用对象是一个可变对象,他的属性会被其他线程修改。

3.使用场景

如果引用对象开始变得难以使用,也许就应该将它改为值对象。引用对象必须被某种方式控制,你总是必须向其控制者请求适当的引用对象。
它们可能造成内存区域之间错综复杂的关联。在分布系统和并发系统中,不可变的值对象特别有用,因为你无需考虑它们的同步问题。

1.2.如何做

  • 检查重构目标是否为不可变对象,或是否可修改为不可变对象。
  • 如果该对象目前还不是不可变的,就使用Remove Setting Method (300),直到它成为不可变的为止。
  • 如果无法将该对象修改为不可变的,就放弃使用本项重构。
  • 建立equals()和hashCode()。
  • 编译,测试。
  • 考虑是否可以删除工厂函数,并将构造函数声明为public。

1.3.示例

我们从一个表示货币种类的Currency类开始

 class Currency...private String _code;public String getCode() {return _code;}private Currency (String code) {_code = code;}

这个类所做的就是保存并返回一个货币种类代码。
它是一个引用对象,所以如果要得到它的实例,必须这么做

Currency usd = Currency.get("USD");

Currency class维护一个实体链表(list of instances);我不能直接使用构造函数创建实体,因为Currency构造函数是private。

new Currency("USD").equals(new Currency("USD")) // returns false

要把一个reference object变成value object,关键动作是:检查它是否为immutable(不可变)。
如果不是,我就不能使用本项重构,因为mutable(可变的)value object会造成令人苦恼的别名现象(aliasing)。
在这里,Currency对象是不可变的,所以下一步就是为它定义equals():

   public boolean equals(Object arg) {if (! (arg instanceof Currency)) return false;Currency other = (Currency) arg;return (_code.equals(other._code));}

如果我定义equals(),我必须同时定义hashCode()。实现hashCode()有个简单办法:读取equals()使用的所有值域的hash codes;然后对它们进行bitwise xor(^)操作。本例中这很容易实现,因为equals()只使用了一个值域:

   public int hashCode() {return _code.hashCode();}

完成这两个函数后,我可以编译并测试。这两个函数的修改必须同时进行,否则倚赖hashing的任何群集对象(collections,例如HashTable、HashSet和HashMap)可能会产生意外行为。
现在,我想创建多少个等值的Currency对象就创建多少个。我还可以把构造函数声明为public,直接以构造函数获取Currency实体,从而去掉Currency class中的factory method和「控制实体创建」的行为。

new Currency("USD").equals(new Currency("USD")) // now returns true