[設計模式]單例模式


簡介


單例模式(Singleton Pattern)保證一個類只有一個實例,並提供一個訪問它的全局訪問點

單例模式是一種對象創建型模式可參考 設計模式 創建型模式)。

單例模式是設計模式中最簡單的模式。它的用途就是使得類的一個對象成為系統中的 唯一實例

 

結構


圖-單例模式結構圖

Singleton : 定義一個接口 Instance () 使得客戶端可以訪問它的唯一實例。


動機


在以下情況中,可以考慮應用單例模式:

  • 保證一個類只有一個實例,並提供一個訪問它的全局訪問點。
  • 當唯一的實例應該對子類可擴展,並且用戶應該可以在不改變代碼的情況下使用擴展的實例。

實際應用場景

一些資源管理器常常設計成單例模式。

在計算機系統中,需要管理的資源包括軟件外部資源,譬如每台計算機可以有若干個打印機,但只能有一個Printer Spooler, 以避免兩個打印作業同時輸出到打印機中。

每台計算機可以有若干通信端口,系統應當集中管理這些通信端口,以避免一個通信端口同時被兩個請求同時調用。任務管理器中難以啟動兩個相同的task。


要點


1、一個類只能有一個實例。
需要定義一個該類的 靜態私有變量,使這個類的所有對象都共用這個實例。 

2、實例必須由類自行創建。

單例模式的類只能提供 私有的構造函數。如此,才能保證外部無法實例化這個類的對象。

3、必須提供獲取實例的方法。

單例模式的類必須提供一個公共的 靜態函數用於創建或獲取它本身的 靜態私有對象


實例


1、懶漢式

你不找懶漢,懶漢根本就懶得去初始化自己的實例。 

instance 初始時沒有初始化,只有當第一次調 getInstance() 時才創建實例。

缺點:當有兩個線程調 getInstance() 方法,當它們同時執行到 if (null == instance) 這行代碼, instance null

繼續向下執行,會生成兩個實例,違背了單例模式的初衷。 

public  class LazySingleton {
     private LazySingleton() {
        System.out.println("Singleton()");
    }

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

2、餓漢式

餓漢根本等不及別人來找他,不管三七二十一先初始化了自身的實例,生怕自己餓着了。

類默認先直接初始化一個實例,以后調用 getInstance() 總是返回這個已創建好的實例。

缺點:在沒有必要獲取實例時,已經預先產生了開銷。

優點:規避了懶漢式方法的線程問題,不用顯示編寫線程安全代碼。
public  class HungerSinleton {
     private HungerSinleton() {
        System.out.println("Singleton()");
    }
    
     private  static HungerSinleton instance =  new HungerSinleton();
    
     public  static HungerSinleton getInstance() {
         return instance;
    }
}

3、雙重鎖的形式

如果既不想在沒有調用 getInstance() 方法時產生開銷,又不想發生線程安全問題,就可以采用雙重鎖的形式。

public  class SyncSingleton {
     private SyncSingleton() {
        System.out.println("Singleton()");
    }
    
     private  static SyncSingleton instance =  null;
    
     public  static SyncSingleton getInstance() {
         if ( null == instance) {
             synchronized(SyncSingleton. class) {
                 if ( null == instance) {
                    instance =  new SyncSingleton();
                }
            }
        }
         return instance;
    }
}

注:在外面判斷了instance實例是否存在,為什么在鎖定后又要在內部又判斷一次?

這是因為,如果 instance null 時有兩個線程同時調用 getInstance(),由於 synchronized 機制,只有一個線程可以進入,另一個需要等待。

這時如果沒有第二道 instance 是否為 null 的判斷,就可能發生第一個線程創建一個實例,而第二個線程又創建一個實例的情況。

C++版單例模式

下面是一個采用餓漢式的例子
#include  " stdafx.h "
#include <iostream>
using  namespace std;

class Singleton
{
private:
     static Singleton *m_instance;
    Singleton()
    {
        cout <<  " Singleton Construct " << endl;
    }
public:
     static Singleton* GetInstance()
    {
         return m_instance;
    }
};

Singleton* Singleton::m_instance =  new Singleton();


int main()
{
     // Singleton *pSingletonA = new Singleton;   // 編譯會報錯,因為不能訪問私有函數
    Singleton *pSingletonA = Singleton::GetInstance();
    Singleton *pSingletonB = Singleton::GetInstance();

     if (pSingletonA == pSingletonB)
        cout <<  " Same Instance " << endl;
     return  0;
}
View Code

推薦閱讀


本文屬於 設計模式系列
 

參考資料


《大話設計模式》

《HeadFirst設計模式》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM