設計模式之旅2--單例模式

1. 定義

確保某個類只有一個實例,而且自行實例化并向整個系統(tǒng)提供這個實例。

單例模式

2. 使用場景

確保某個類只有一個實例對象,避免產(chǎn)生多個對象消耗過多的資源;或者邏輯上某個類的對象應該只有一個,出現(xiàn)多個則出現(xiàn)一些錯誤。例如:

  1. 對IO、數(shù)據(jù)庫、網(wǎng)絡、圖片、SharePreference等的訪問
  2. 需要定義大量的靜態(tài)常量和靜態(tài)方法,例如Utils類
  3. 唯一序列號生成的場合
  4. 需要一個共享訪問點或者共享數(shù)據(jù)的場合,例如全局的計數(shù)器
  5. Android中的SystemService就是通過單例的方式注冊到系統(tǒng)當中

3. 實現(xiàn)

3.1 懶漢式

/**
 * 懶漢式
 * 特點:Lazy初始化;線程安全,但是由于每次需要同步性能較低,不建議使用
 */
public class Singleton {

    private static Singleton sInstance;

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        if (sInstance == null) {
            sInstance = new Singleton();
        }
        return sInstance;
    }

}

3.2 雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

/**
 * 雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)
 * 特點:懶漢式的改進版,Lazy初始化;線程安全,且在多線程情況下能保持高性能
 */
public class Singleton {

    private static Singleton sInstance;

    private Singleton() {

    }

    public static Singleton getInstance() {
        if (sInstance == null) {
            synchronized (Singleton.class) {
                if (sInstance == null) {
                    sInstance = new Singleton();
                }
            }
        }
        return sInstance;
    }

}

3.3 餓漢式

/**
 * 餓漢式
 * 特點:非Lazy初始化,浪費內(nèi)存;線程安全,基于ClassLoader機制避免了多線程的同步問題
 */
public class Singleton {

    //方式一:類裝載的時候初始化
    private static Singleton sInstance = new Singleton();

    //方式二:類初始化的時候才去初始化
    static {
        sInstance = new Singleton();
    }

    private Singleton() {

    }

    public static synchronized Singleton getInstance() {
        return sInstance;
    }

}

3.4 靜態(tài)內(nèi)部類

/**
 * 靜態(tài)內(nèi)部類
 * 特點:餓漢式只要類裝載或者類初始化的時候單例初始化,但是靜態(tài)內(nèi)部類的方式確保調(diào)用getInstance才Lazy初始化;線程安全;推薦使用
 */
public class Singleton {

    private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();
    }

    private Singleton() {
        
    }

    public static Singleton getInstance() {
        return SingletonHolder.sInstance;
    }
    
}

3.5 枚舉

/**
 * 枚舉
 * 特點:Lazy初始化;線程安全;這種實現(xiàn)方式還沒有被廣泛采用,但這是實現(xiàn)單例模式的最佳方法。它更簡潔,自動支持序列化機制,絕對防止多次實例化。
 */
public enum Singleton {

    INSTANCE

}

3.6 通過容器來實現(xiàn)

/**
 * 通過容器來實現(xiàn)
 * 特點:通過特定時機(例如程序初始化)將單例注入到容器當中,使用的時候通過key來獲?。唤档土笋詈隙?,提高易用性
 */
public class Singleton {

    public static class SingletonManager {

        private SingletonManager() {

        }

        private static Map<String, Singleton> sSingletonMap = new HashMap<>();

        public static void register(String key, Singleton value) {
            if (!sSingletonMap.containsKey(key)) {
                sSingletonMap.put(key, value);
            }
        }

        public static void unregister(String key) {
            if (sSingletonMap.containsKey(key)) {
                sSingletonMap.remove(key);
            }
        }


        public static Singleton getSingleton(String key) {
            return sSingletonMap.get(key);
        }

    }

}

3.7 Kotlin中通過object關鍵字實現(xiàn)

/**
 * Kotlin中通過object關鍵字可以實現(xiàn)最簡單的單例,相當于餓漢式
 * 特點:這種單例只有一個實現(xiàn)的對象;不能自定義構造方法;可以實現(xiàn)接口、繼續(xù)父類
 */
object Singleton {
    public fun test() {
        println("")
    }
}

//調(diào)用方式
fun main(args: Array<String>) {
    Singleton.test();
}

4. 優(yōu)點

  1. 由于單例模式在內(nèi)存中只有一個實例,減少了內(nèi)存開支,特別是一個對象需要頻繁的被創(chuàng)建、銷毀,而且創(chuàng)建或銷毀時性能又無法優(yōu)化,單例模式的優(yōu)勢就非常明顯。
  2. 由于單例模式只生成一個實例,減少了系統(tǒng)性能開銷,當一個對象的產(chǎn)生需要比較多的資源時,如讀取配置、產(chǎn)生其他依賴對象時,則可以通過在應用啟動時直接產(chǎn)生一個單例對象,然后永久駐留內(nèi)存的方式來解決(在Java EE中采用單例模式時需要注意JVM垃圾回收機制);
  3. 單例模式可以避免對資源的多重占用,例如一個寫文件動作,由于只有一個實例存在內(nèi)存中,避免對同一個資源文件的同時寫操作。
  4. 單例模式可以在系統(tǒng)設置全局的訪問點,優(yōu)化環(huán)共享資源訪問,例如可以設計一個單例類,負責所有數(shù)據(jù)表的映射處理。

5. 缺點

  1. 單例模式?jīng)]有接口,擴展很困難,若要擴展,除了修改代碼沒有第二種途徑可以實現(xiàn)。單例模式為什么不能增加接口呢?因為接口對單例模式是沒有任何的意義,它要求“自行實例化”,并且提供單一實例、接口或抽象類是不可能被實例化的。
  2. 單例模式對測試是不利的。在并行開發(fā)環(huán)境中,如果單例模式?jīng)]有完成,是不能進行測試的,沒有接口也不能使用mock的方式虛擬一個對象。
  3. 單例模式與單一職責原則有沖突。一個類應該只實現(xiàn)一個的邏輯,而不關心它是否是單例的,決定它是不是要單例是環(huán)境決定的,單例模式把“要單例”和業(yè)務邏輯融合也在一個類中。
  4. 通常來說,單例對象如果持有Context,很容易引發(fā)內(nèi)存泄漏。此時需要注意傳遞給單例對象的Context是ApplicationContext。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容