Singleton Pattern :

限制類別只能產生一個實體並提供全域存取方法。


 
Singleton Pattern 大致上可以分為 2 種形式,飢餓型以及懶人型,而不論哪種形式都相當簡單。
飢餓型,在第3行宣告 sUniqueInstance 變數立即產生實體。

public class HungerSingleton
{
    private static HungerSingleton sUniqueInstance = new HungerSingleton();
    private HungerSingleton() {
    }
    public static HungerSingleton getInstance()
    {
        return sUniqueInstance;
    }
}

 


 
懶人型,在第3行宣告 sUniqueInstance 變數並不馬上建立實體,而是等到需要實體才建立(第12行)。

public class LazySingleton
{
    private static LazySingleton sUniqueInstance;
    private LazySingleton(){
    }
    public static LazySingleton getInstance()
    {
        if (sUniqueInstance == null) {
            sUniqueInstance = new LazySingleton();
        }
        return sUniqueInstance;
    }
}

 
使用 Singleton Pattern 需要注意的是多執行緒的問題。
因為使用靜態初始化的關係,HungerSingleton 可以確保只產生一個 instance。
LazySingleton 由於使用判斷 null 值來產生實體,當 2 條執行緒同時執行到第11,12行,就有可能產生 2 個 Singleton ,就不是單例了。
最簡單的解決方式可以加上 synchronized 來限制同時只能有一條執行緒進入 getInstance 方法 如下

    public static synchronized LazySingleton getInstance()
    {
        if (sUniqueInstance == null) {
            sUniqueInstance = new LazySingleton();
        }
        return sUniqueInstance;
    }

另一種比較麻煩的解決方法為DCL(double checked locking), 但這種解法有個限制就是只能支援 jvm 1.5之後的版本。

public class DCLSingleton {
    private static volatile DCLSingleton sUniqueInstance;
    private static Object mLock = new Object();
    public static DCLSingleton getInstance() {
        if (sUniqueInstance == null) {
            synchronized (mLock) {
                if (sUniqueInstance == null) {
                    sUniqueInstance = new DCLSingleton();
                }
            }
        }
        return sUniqueInstance;
    }
}

如果情況予許還是選擇 HungerSingleton 來避免可能產生多個實體的情況,但 HungerSingleton 有另外一個缺點。
因為沒有實現 lazy load , 若其本身佔用大量記憶體,會在程序開始的時候造成浪費記憶體的情況。
可以使用靜態內部類別(static inner class)來實現 lazy load + HungerSingleton

public class InnerStaticHungerSingleton
{
    private InnerStaticHungerSingleton()
    {
      //Prevents utility class being instantiated
    }
    private static class InnerInstance{
        private static final InnerStaticHungerSingleton UNIQUE_INSTANCE = new InnerStaticHungerSingleton();
    }
    public static InnerStaticHungerSingleton getInstance()
    {
        return InnerInstance.UNIQUE_INSTANCE;
    }
}

 


另外 LazySingleton 實現了 lazy load 作法。也就是為了節省記憶體使用空間,只有在變數需要實體才產生。
除了 lazy load 可以節省記憶體之外,還有另一個技術 cache 可以達到相同的效果。
simple cache in java

public class SimpleCache {
    private Map<String, Object> mCache = new HashMap<String, Object>();
    private static final String KEY_OF_DATA = "keyOfData";
    public Object getCache() {
        Object data = mCache.get(KEY_OF_DATA);
        if (data == null) {
            data = new Object();
            mCache.put(KEY_OF_DATA, data);
        }
        return data;
    }
}

簡單的 cache 實作,主要使用 Map 來達成,第一次呼叫 getCache 會進入第10行,並把 data 存入 map 中,隨後需要使用的時候再從 map 取出即可。
不需要重複產生實體,其實再複雜一點的變化型就是 Flyweight Pattern(享元模式),其作用也是解決需要大量細微性物件時,記憶體不夠的狀態。