爲什麼使用不可變對象
在大型的軟件開發過程中最大的問題是代碼的複雜性。代碼的可讀性可能是首要目標。可讀性差的代碼讓人很難對代碼的正確性快速的做出判斷。
可變對象是增加還是減少了代碼的可讀性呢?
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消耗
另一個問題是一些對象本身就是具有可變屬性,試圖將這些對象變成不可變對象往往得不償失。
雖然存在這些限制,但是大部分情況下使用不可變對象還是十分有用的。不管使用哪種設計,都需要權限利弊,考慮得失,選擇當前場景下最適合的。