1.什麼是單例模式
單例模式是一種常用的軟體設計模式。在它的核心結構中只包含一個被稱為單例類的特殊類。通過單例模式可以保證系統中一個類只有一個執行個體而且該執行個體易於外界訪問,從而方便對執行個體個數的控制並提供全域訪問點。
2.單例的使用
教科書中的樣本是很簡單的,要想實現一個單例類,只需要三點:
- 定義私人的靜態成員
- 私人化構造方法
- 提供公有的靜態Factory 方法
典型的代碼是:
public class Singleton {private static Singleton instance;private Singleton(){}public static Singleton getInstance(){if (instance==null){instance=new Singleton();}return instance;}}
但常常我們還要面對更複雜的現實問題。
2.1.全部使用靜態成員
有時我們會看到一個Singleton的成員變數全部是static類型的,這種情況下,建議直接刪除構造方法和靜態Factory 方法,直接作為一個工具類就好了。因為這個類已經不需要對象了,何必多此一舉呢?
2.2.對構造方法不進行私人化處理
構造方法不進行私人化,就意味著client可以自由的通過new Singleton()構造新的執行個體,這已經有違了採用單例模式的本意,不如把靜態Factory 方法刪掉吧,放棄單例的需求。如果有理由讓一個類既可以是單例,也可以是多例,我想應該仔細設計了。
2.3.靜態Factory 方法需要參數
我們時常會看到這樣的構造方法:
public static Singleton getInstance(Context context)
如果一個執行個體的構造需要參數,那麼就意味著傳遞不同的參數將影響到單例的狀態。比如我們看這樣一段代碼。
public class Singleton {private static Singleton instance;private Object value;private Singleton(Object value){this.value=value;}public static Singleton getInstance(Object value){if (instance==null){instance=new Singleton(value);}return instance;}public void fun(){//value.doSomething();}}
在程式中的某處調用時使用了Singleton.getInstance(objA).fun();另一處使用了Singleton.getInstance(objB).fun();顯然,同樣一個Singleton執行個體,在運行時出現了未預期的行為。這與設計單例時我們期望的可控性是不符的,你無法預知在下一次使用value時真正的行為。
2.4.派生子類
如果需要在繼承樹中使用單例,只要將基類的建構函式由private調整為protected即可,但問題會很多,這個有很多文章來討論了,在此略過。
簡而言之,如果在基類中實現靜態Factory 方法,必然會與子類產生依賴,即發生父類依賴於子類的非正常情況。如果在子類中實現靜態Factory 方法,則整個設計就出現了父類無法約束子類的建立情況,也就是說可能出現違背設計意圖的實現。建議對單例類不要再派生。
2.5.多線程問題
既然在軟體運行期內只有一個執行個體,由於無法限制client的調用時機,就不可避免要處理互斥問題。
先來看靜態Factory 方法的互斥處理,典型的代碼是:
public static Singleton getInstance(){if (instance==null){synchronized (Singleton.class) {if (instance==null){instance=new Singleton();}}}return instance;}
這段代碼既完成了在構造這個唯一執行個體時的保護,又在構造完以後的多次擷取執行個體時不做無謂的互斥處理以提高運行效率,是目前比較推薦的實現方法。
再來看類的其他成員的互斥問題,這個就與其他class是一樣的了,需要小心的處理多線程訪問的影響。
2.6.記憶體回收
單例的記憶體問題也是值得關注的,一量單例建立以後,靜態變數instance就會持有一份記憶體引用,而且由於其static性質,這份記憶體將在程式運行期間持續佔用,無法通過GC進行回收。所以對記憶體敏感的程式要減少對單例的使用,或者妥善處理記憶體回收問題。
3.小結
總之,雖然單例看上去很美,但還是有這麼多需要認真思考、妥善決策的技術要點,用好單例並不像我們在教科書中看到的那樣簡單。
——歡迎轉載,請註明出處 http://blog.csdn.net/caowenbin ——