最近在看《 重构改善既有代码设计》其中一小节讲述了值对象与引用对象的转换,这里结合自身理解进行一点阐释。
概念简介
值对象
现实世界中一种信息的体现,我们只关注其数据值。需要equals()来比较是否相等。就好比两枚硬币,我只关心它所表现出来的价值,显然两枚硬币是一样的。
引用对象
现实世界中的一个实物,可以通过==直接比较两个对象是否相等。因为即使完全复刻了另外一个实体的信息,本质上两者也是不一样的。
两者如何选择
值对象
如果某个对象是不可变,无论何时调用同一对象的同一个查询函数都应该得到同样的结果。简单点说就是一个对象在使用期间,都应该保持状态的一致,不能被其他线程改变。如果使用过JUC并发的时候有时候IDE会提醒“变量在使用期间可能会被修改,需要重新声明一个final对象。”就是不可变性的体现。
引用对象
如果需要一个对象的修改都能影响到引用此对象的地方,就应该变为引用对象。因为引用对象大家用的是同一个地址空间存的数据,自然牵一发而动全身了。
如工厂模式、享元模式等就是很好的引用模式的体现。
内存空间的使用
值对象
值对象会在每次new的时候都去堆中开辟自己的空间,来存储数据。那么堆的压力相对大一些。
引用对象
引用对象在创建的时候会在堆中创建,也就是把真正的数据存在了堆中,之后的创建都是在栈中创建一个指向该数据的引用罢了。显然栈的压力大一些。
简单案例
值对象
equals()和hashCode()必须成对出现!
/**
* 之后可以创建无数多个Currency 只要code相同,就认为是相同的
**/
class Currency{
private String code;
public String getCode(){return code;}
public Currency(String code){this.code = code;}
public boolean equals(Object object){
if (!(object instanceof Currency)) return false;
Currency other = (Currency) object;
return code.equals(other.code);
}
public int hashCode(){return this.code.hashCode();}
}
public static void main(String[] args){
new Currency("USD").equals(new Currency("USD")); // return true;
}
引用对象
这段代码中,用到了工厂模式,对象仅创建了一次,后续都是都那唯一对象的引用。
class Customer{
// ...省略部分代码
private static Dictionary instances = new Hashtable();
static void loadCustomers(){
new Customer("Lemon Car Hire").store();
new Customer("Associated Coffee Machines").store();
new Customer("Bilston Gasworks").store();
}
private void store(){
instances.put(this.getName(),this)
}
private String getName(){
return name;
}
public static Customer getNamed(String name){
return (Customer) instances.get(name);
}
private Customer(String name){
this.name = name;
}
}
class Order{
public Order(String customer){
customer = Customer.create(customer);
}
}