Java設計模式之從[反恐精英控制檯]分析單例(Singleton)模式

  所謂單例模式(Singleton),就是保證一個類僅有一個實例,並提供一個訪問它的全局訪問點的模式。

  玩過反恐精英的同學應該都知道,遊戲當中是有個控制檯的,我們可以通過按`鍵(波浪線鍵)調出這個控制檯。控制檯的目的是爲了方便開發人員調試,當然我們也可以在裏面來修改一些遊戲參數,如輸入SV_GRAVITY 100可以把重力調整到100,那麼我們跳躍的高度就是原來的8倍了。

  由於控制檯的遊戲的全局通用的,因此我們希望這個控制檯類僅有一個實例。當我們訪問它的時候,如果它沒有實例化,則實例化之,如果它實例化了我們則返回它實例化的對象。這便是單例模式。

  那麼,以Java爲例,我們應該在何時將類的對象實例化呢?是在第一次加載類的時候?第一次需要返回實例的時候?因爲對象的實例化時間順序的差異,我們可以寫出幾種單例模式的實現方法,本篇文字以懶漢、餓漢、嵌套類(內部靜態類)三種方法爲例。代碼如下:

class Log {
    public void print(String str){
        System.out.println(str + " - From 懶漢" );
    }
    private static Log logInstance;
    private Log(){}
    public static synchronized Log getInstance(){
        if (logInstance == null){
            logInstance = new Log();
        }
        return logInstance;
    }
}

class Log2{
    private static Log2 logInstance = new Log2();
    private Log2(){}
    public void print(String str){
        System.out.println(str + " - From 餓漢" );
    }
    public static Log2 getInstance(){
        return logInstance;
    }
}

class Log3{
    private static class LogHolder{
        private static final Log3 logInstance = new Log3();
    }
    private Log3(){}
    public static Log3 getInstance(){
        return LogHolder.logInstance;
    }
    public void print(String str){
        System.out.println(str + " - From 內部靜態類" );
    }
}

class Singleton
{
    public static void main(String[] args) {
        Log log = Log.getInstance();
        log.print("Hello world");
        Log2 log2 = Log2.getInstance();
        log2.print("Hello world");
        Log3 log3 = Log3.getInstance();
        log3.print("Hello world");
    }
}

  在上面的例子中,Log、Log2和Log3分別用懶漢、餓漢、內部靜態類實現了單例模式,它們能夠調用print方法,輸出我們傳入的字符串。下面簡要來分析一下這3個類之間的差異。

  Log類可以調用getInstance來返回一個Log實例,如果這個實例沒有被創建,則進行創建,否則直接返回實例,這個就是懶漢模式(需要的時候才進行判斷)。由於判斷是否存在和創建存在時間差,因此我加上了synchronized關鍵字保證它不會在多線程中遭到爭搶。這樣寫的代價是,系統會在同步鎖上有很大的開銷,假設很多地方需要使用到getInstance,那麼時間開銷會很大。

  Log2類,將實例化寫在了一個static語句中,那麼,只要這個類被加載(這個類第一次被使用)時,就會創建logInstance對象。假設這個對象十分龐大,那麼我們在加載這個類的時候會花很多時間。另外,假設new Log2()執行失敗,我們將永遠無法得到Log2的實例。只要這個類被加載,則對象被創建,這個就是餓漢。

  Log3類包含一個嵌套類(內部靜態類),它是Log2類的升級版。我們可以看到,logInstance的實例化被寫到了一個嵌套類中,那麼這個嵌套類被加載的時間也就是調用Log3.getInstance的時間,也就是說,只有我們調用了Log3.getInstance,這個對象纔會被創建。

  以上程序的運行結果爲:

Hello world - From 懶漢
Hello world - From 餓漢
Hello world - From 內部靜態類

  當一個類只期望有一個實例,且客戶可以從一個衆所周知的地方訪問它時,就可以考慮單例模式了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章