在閻宏博士的《JAVA與模式》一書中開頭是這樣描述不變(Immutable)模式的:
一個對象的狀態在對象被建立之後就不再變化,這就是所謂的不變模式。
不變模式的結構
不變模式可增強對象的強壯型(robustness)。不變模式允許多個對象共用某一個對象,降低了對該對象進行並發訪問時的同步化開銷。如果需要修改一個不變對象的狀態,那麼就需要建立一個新的同類型對象,並在建立時將這個新的狀態儲存在新對象裡。
不變模式只涉及到一個類。一個類的內部狀態建立後,在整個生命週期都不會發生變化時,這樣的類稱作不變類。這種使用不變類的做法叫做不變模式。不變模式有兩種形式:一種是弱不變模式,另一種是強不變模式。
弱不變模式
一個類的執行個體的狀態是不可改變的;但是這個類的子類的執行個體具有可能會變化的狀態。這樣的類符合弱不變模式的定義。要實現弱不變模式,一個類必須滿足下麵條件:
第一、所考慮的對象沒有任何方法會修改對象的狀態;這樣一來,當對象的建構函式將對象的狀態初始化之後,對象的狀態便不再改變。
第二、所有屬性都應當是私人的。不要聲明任何的公開的屬性,以防用戶端對象直接修改任何的內部狀態。
第三、這個對象所引用到的其他對象如何是可變對象的話,必須設法限制外界對這些可變對象的訪問,以防止外界修改這些對象。如何可能,應當盡量在不變對象內部初始化這些被引用的對象,而不要在用戶端初始化,然後再傳入到不變對象內部來。如果某個可變對象必須在用戶端初始化,然後再傳入到不變對象裡的話,就應當考慮在不變對象初始化的時候,將這個可變對象複製一份,而不要使用原來的拷貝。
弱不變模式的缺點是:
第一、一個弱不變對象的子物件可以是可變對象;換言之,一個弱不變對象的子物件可能是可變的。
第二、這個可變的子物件可能可以修改父物件的狀態,從而可能會允許外界修改父物件的狀態。
強不變模式
一個類的執行個體不會改變,同時它的子類的執行個體也具有不可變化的狀態。這樣的類符合強不變模式。要實現強不變模式,一個類必須首先滿足弱不變模式所要求的所有條件,並且還有滿足下麵條件之一:
第一、所考慮的類所有的方法都應當是final,這樣這個類的子類不能夠置換掉此類的方法。
第二、這個類本身就是final的,那麼這個類就不可能會有子類,從而也就不可能有被子類修改的問題。
“不變"和"唯讀"的區別
"不變"(Immutable)與"唯讀"(Read Only)是不同的。當一個變數是”唯讀“時,變數的值不能直接改變,但是可以在其他變數發生改變的時候發生改變。
比如,一個人的出生年月日是”不變“屬性,而一個人的年齡便是”唯讀“屬性,不是”不變“屬性。隨著時間的變化,一個人的年齡會隨之發生變化,而人的出生年月日則不會變化。這就是”不變“和“唯讀”的區別。
不變模式在JAVA中的應用
不變模式在JAVA中最著名的應用便是java.lang.String類。String類是一個強不變類型,在出現如下的語句時:
String a = "test"; String b = "test"; String c = "test";
JAVA虛擬機器其實只會建立這樣一個字串的執行個體,而這三個String對象都在共用這一個值。
不變模式的優點和缺點
不變模式有很明顯的優點:
(1)因為不能修改一個不變對象的狀態,所以可以避免由此引起的不必要的程式錯誤;換言之,一個不變的對象要比可變的對象更加容易維護。
(2)因為沒有任何一個線程能夠修改不變對象的內部狀態,一個不變對象自動就是安全執行緒的,這樣就可以省掉處理同步化的開銷。一個不變對象可以自由地被不同的用戶端共用。
不變模式的缺點:
不變模式唯一的缺點是:一旦需要修改一個不變對象的狀態,就只好建立一個新的同類對象。在需要頻繁修改不變對象的環境裡,會有大量的不變對象作為中間結果被建立出來,再被JAVA垃圾收集器收集走。這是一種資源上的浪費。
在設計任何一個類的時候,應當謹慎考慮其狀態是否有需要變化的可能性。除非其狀態有變化的必要,不然應當將它設計成不變類。