Android設計模式之單例模式

設計模式

設計模式(design pattern)是對軟件設計中廣泛存在(反覆出現)的各類問題,所提出的解決方案。這個術語是由埃裏希·伽瑪(Erich Gamma)等人在1990年代從建築設計領域引入到計算機科學的。《設計模式》一書原先把設計模式分爲建立型模式、結構型模式、行爲型模式,把它們經過受權、聚合、診斷的概念來描述。javascript

建立範例

建立範例所有是關於如何建立實例的。這組範例能夠被劃分爲兩組:類建立範例及對象建立範例。類建立實例在實例化過程當中有效的使用類之間的繼承關係,對象建立範例則使用代理來完成其任務。包括:java

  • 抽象工廠(Abstact Factory)
  • 構造器(Builder Pattern)
  • 工廠方法(Factory Method pattern)
  • 原型(Prototype pattern)
  • 單例模式(Singleton pattern)

結構範例

這組範例都是關於類及對象複合關係的。包括:android

  • 適配器(Adapter pattern)
  • 橋接(Bridge pattern)
  • 組合(Composite pattern)
  • 裝飾(Decorator pattern)
  • 外觀(Façade pattern)
  • 享元(Flyweight pattern)
  • 代理(Proxy pattern)

行爲範例

這組範例都是關於對象之間如何通信的。包括:設計模式

  • 職責鏈(Chain-of-responsibility pattern)
  • 命令(Command pattern)
  • 翻譯器(Interpreter pattern)
  • 迭代器(Iterator pattern)
  • 仲裁器(Mediator pattern)
  • 回憶(Memento pattern)
  • 觀察者(Observer pattern)
  • 狀態機(State pattern)
  • 策略(Strategy pattern)
  • 模板方法(Template method pattern)
  • 參觀者(Visitor)

單例模式

什麼是單例模式?確保一個類只有一個實例,並提供對該實例的全局訪問,其構造函數私有化。安全

七種實現方式

各類寫法各有利弊,讓咱們看看具體寫法:併發

懶漢模式,線程不安全

public class SingletonPattern {

    private static SingletonPattern singletonPattern = null;

    private SingletonPattern() {
    }

    public static SingletonPattern getInstance() {
        if (singletonPattern == null) {
            singletonPattern = new SingletonPattern();
        }
        return singletonPattern;
    }
}複製代碼

類加載時只是申明實例,並未實例化,當調用getInstance方法,才進行實例化,但線程不安全,多個線程併發調用getInstance方法可能會致使建立多份相同的單例出來,解決的辦法就是使用synchronized關鍵字。app

懶漢模式,線程安全

public class SingletonPattern {

    private static SingletonPattern singletonPattern = null;

    public static SingletonPattern getInstance() {
        synchronized (SingletonPattern.class) {
            if (singletonPattern == null)
                singletonPattern = new SingletonPattern();
        }
        return singletonPattern;
    }
}複製代碼

synchronized保證一個時間內只能有一個線程獲得執行,另外一個線程必須等待當前線程執行完才能執行,使得線程安全。缺點每次調用getInstance方法都進行同步,形成了沒必要要的同步開銷。函數

餓漢模式

public class SingletonPattern {
    //餓漢模式
    private static final SingletonPattern singletonPattern = new SingletonPattern();

    private SingletonPattern() {
    }

    public static SingletonPattern getInstance() {
        return singletonPattern;
    }
}複製代碼

類加載時就已經進行實例化,類加載較慢,但獲取對象速度快,線程安全。oop

雙重校驗DCL模式

public class SingletonPattern {
    private static volatile SingletonPattern singletonPattern = null;

    private SingletonPattern() {
    }

    public static SingletonPattern getInstance() {
        //第一層校驗,爲了不沒必要要的同步
        if (singletonPattern == null) {
            synchronized (SingletonPattern.class) {
                //第二層校驗,實例null的狀況下才建立
                if (singletonPattern == null)
                    singletonPattern = new SingletonPattern();
            }
        }
        return singletonPattern;
    }
}複製代碼

這裏使用了volatile關鍵字,由於多個線程併發時初始化成員變量和對象實例化順序可能會被打亂,這樣就出錯了,volatile能夠禁止指令重排序。雙重校驗雖然在必定程度解決了資源的消耗和多餘的同步,線程安全問題,但在某些狀況仍是會出現雙重校驗失效問題,即DCL失效。ui

靜態內部類單例模式

public class SingletonPattern {

    private SingletonPattern() {
    }

    private static class SingletonPatternHolder {
        private static final SingletonPattern singletonPattern = new SingletonPattern();
    }

    public static SingletonPattern getInstance() {
        return SingletonPatternHolder.singletonPattern;
    }
}複製代碼

第一次調用getInstance方法時加載SingletonPatternHolder 並初始化singletonPattern,這樣不只能確保線程安全,也能保證SingletonPattern類的惟一性。

使用容器

SingletonManager

public class SingletonManager {
    private SingletonManager() {
    }

    private static Map<String, Object> instanceMap = new HashMap<>();

    public static void registerInstance(String key, Object instance) {
        if (!instanceMap.containsKey(key)) {
            instanceMap.put(key, instance);
        }
    }

    public static Object getInstance(String key) {
        return instanceMap.get(key);
    }
}複製代碼

SingletonPattern

public class SingletonPattern {
    SingletonPattern() {
    }

    public void doSomething() {
        Log.d("wxl", "doSomeing");
    }
}複製代碼

代碼調用:

SingletonManager.registerInstance("SingletonPattern", new SingletonPattern());
SingletonPattern singletonPattern = (SingletonPattern) SingletonManager.getInstance("SingletonPattern");
singletonPattern.doSomething();複製代碼

根據key獲取對象對應類型的對象,隱藏了具體實現,下降了耦合度。

枚舉單例模式

public enum SingletonEnum {
    INSTANCE;

    public void doSomething() {
        Log.d("wxl", "SingletonEnum doSomeing");
    }
}複製代碼

代碼調用:

SingletonEnum.INSTANCE.doSomething();複製代碼

更加簡潔,線程安全,還能防止反序列化致使從新建立新的對象,而以上方法還需提供readResolve方法,防止反序列化一個序列化的實例時,會建立一個新的實例。枚舉單例模式,咱們可能使用的不是不少,但《Effective Java》一書推薦此方法,說「單元素的枚舉類型已經成爲實現Singleton的最佳方法」。不過Android使用enum以後的dex大小增長不少,運行時還會產生額外的內存佔用,所以官方強烈建議不要在Android程序裏面使用到enum,枚舉單例缺點也很明顯。

Android應用示例

AccessibilityManager

public final class AccessibilityManager {
    static final Object sInstanceSync = new Object();
    private static android.view.accessibility.AccessibilityManager sInstance;
    public static AccessibilityManager getInstance(Context context) {
        synchronized (sInstanceSync) {
            if (sInstance == null) {
                //省略部分代碼
                sInstance = new AccessibilityManager(context, null, userId);
            }
        }
        return sInstance;
    }
}複製代碼

InputMethodManager

public final class InputMethodManager {
    static android.view.inputmethod.InputMethodManager sInstance;
    public static InputMethodManager getInstance() {
        synchronized (InputMethodManager.class) {
            if (sInstance == null) {
                //省略部分代碼
                sInstance = new InputMethodManager(service, Looper.getMainLooper());
            }
            return sInstance;
        }
    }
}複製代碼

參考

廣而告之

我開通了小密圈,鏈接彼此,一塊兒進步~

什麼是小密圈?小密圈是一個擁有共同話題、目標的人在一塊兒使用的朋友圈。它做爲高品質知識社羣,需付費加入。

爲何我要建立小密圈,是爲個人讀者們建立一個更好的互動空間,讓讀者能夠很方便聯繫到另外一個讀者。

爲何是收費的,收費是最好的門檻,是一種很好的篩選機制。

這裏咱們除了創建聯繫,我還會不按期分享一些過來人的經驗和總結,不止於Android 。

相關文章
相關標籤/搜索