为什么使用不可变对象
在大型的软件开发过程中最大的问题是代码的复杂性。代码的可读性可能是首要目标。可读性差的代码让人很难对代码的正确性快速的做出判断。
可变对象是增加还是减少了代码的可读性呢?
QueryObject queryObject =newQueryObject(name, page, pageSize);
Collection<Customter> customters = seach(queryObject);
if(customters.isEmpty()) {
adjustSerachCriteria(queryObject, name);
search(queryObject);
}
上面这段代码中第二次search的时候很难判断queryObject到底有没有改变。需要根据第一次有没有查询到数据以及adjustSerachCriteria的实现才能最终判断出来。
将上面的代码重构成下面的方式:
QueryObject queryObject =newQueryObject(name, page, pageSize);
Collection<Customter> customters = seach(queryObject);
if(customters.isEmpty()) {
QueryObject newQueryObject = adjustSerachCriteria(queryObject, name);
search(newQueryObject);
}
上面的代码明确的adjustSerachCriteria方法会创建一个新的查询对象进行search操作。
可变对象有哪些缺点:
很难定位一个对象的数据有没有被修改(一个方法调用有可能修改了入参,增加了方法的理解成本,一个方法除了返回值还对程序产生了其它影响) 阅读代码的层级改变了;在写代码时一个方法里面的代码层级应该是一致的,如果在调用的方法里面修改了对象数据,在阅读代码,很难按照一个顺序去阅读,因为除了阅读方法本身之外,还需要阅读方法调用的其它方法 如果是多线程的代码,大大的增加了代码问题的定位与debug难度
如何使用不可变对象
如果类型比较简单,可以直接将类型定义成值对象(value object),下面是一个通过值对象来构建不可变对象的方法
public class Product {
private String name;
private int amount;
private long price;
public Product(String name, int amount, long price) {
super();
this.name = name;
this.amount = amount;
this.price = price;
}
public String getName() {
return name;
}
public int getAmount() {
return amount;
}
public long getPrice() {
return price;
}
}
通过创建私有构造方法与只读对象来实现不可变对象
当卖出一个产品时,不是在原有的product对象上面将数量减1,而是新建一个product的对象。
public class Product {
//同上
public Product saleOne() {
return new Product(name, amount -1, price);
}
}
不可变对象有如下好处:
可以将校验逻辑收敛到构造函数中
对象总是处于有效的状态
对象是线程安全的
增加了代码的可读性,因为不需要跳跃到调用的方法内部去确定对象的那些成员被修改了
不可变对象的限制:
由于期望改变不可变对象只有通过复制的方式来实现,所以在比较简单跟小的类型的时候比较适用,如果一个对象很大,则复制对象会带来性能问题,增加内存的使用与cpu消耗
另一个问题是一些对象本身就是具有可变属性,试图将这些对象变成不可变对象往往得不偿失。
虽然存在这些限制,但是大部分情况下使用不可变对象还是十分有用的。不管使用哪种设计,都需要权限利弊,考虑得失,选择当前场景下最适合的。
网友评论