Singleton Pattern
5. 單例模式
5.1. 模式動機
對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個打印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。
如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。
一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。
5.2. 模式定義
單例模式(Singleton Pattern):單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。
單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象創建型模式。單例模式又名單件模式或單態模式。
5.3. 模式結構
單例模式包含如下角色:
- Singleton:單例
5.4. 時序圖
5.5. 代碼分析
#include <iostream>
#include "Singleton.h"
using namespace std ;
int main ( int argc , char * argv [])
{
Singleton * sg = Singleton :: getInstance (); sg -> singletonOperation (); return 0 ; }
////////////////////////////////////////////////// /////////
// Singleton.cpp
// Implementation of the Class Singleton
// Created on: 02-十月-2014 17:24:46
// Original author: colin
/////// ////////////////////////////////////////////////// //
#include "Singleton.h"
#include <iostream>
using namespace std ;
Singleton * Singleton :: instance = NULL ;
Singleton :: Singleton (){
}
Singleton ::~ Singleton (){
delete instance ;
}
Singleton * Singleton :: getInstance (){
if ( instance == NULL ) { instance = new Singleton (); } return instance ; }
void Singleton :: singletonOperation (){
cout << "singletonOperation" << endl ;
}
5.6. 模式分析
單例模式的目的是保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。單例模式包含的角色只有一個,就是單例類——Singleton。單例類擁有一個私有構造函數,確保用戶無法通過new關鍵字直接實例化它。除此之外,該模式中包含一個靜態私有成員變量與靜態公有的工廠方法,該工廠方法負責檢驗實例的存在性並實例化自己,然後存儲在靜態成員變量中,以確保只有一個實例被創建。
在單例模式的實現過程中,需要注意如下三點:
- 單例類的構造函數為私有;
- 提供一個自身的靜態私有成員變量;
- 提供一個公有的靜態工廠方法。
5.7. 實例
在操作系統中,打印池(Print Spooler)是一個用於管理打印任務的應用程序,通過打印池用戶可以刪除、中止或者改變打印任務的優先級,在一個系統中只允許運行一個打印池對象,如果重複創建打印池則拋出異常。現使用單例模式來模擬實現打印池的設計。
5.8. 優點
- 提供了對唯一實例的受控訪問。因為單例類封裝了它的唯一實例,所以它可以嚴格控制客戶怎樣以及何時訪問它,並為設計及開發團隊提供了共享的概念。
- 由於在系統內存中只存在一個對象,因此可以節約系統資源,對於一些需要頻繁創建和銷毀的對象,單例模式無疑可以提高系統的性能。
- 允許可變數目的實例。我們可以基於單例模式進行擴展,使用與單例控制相似的方法來獲得指定個數的對象實例。
5.9. 缺點
- 由於單例模式中沒有抽象層,因此單例類的擴展有很大的困難。
- 單例類的職責過重,在一定程度上違背了“單一職責原則”。因為單例類既充當了工廠角色,提供了工廠方法,同時又充當了產品角色,包含一些業務方法,將產品的創建和產品的本身的功能融合到一起。
- 濫用單例將帶來一些負面問題,如為了節省資源將數據庫連接池對象設計為單例類,可能會導致共享連接池對象的程序過多而出現連接池溢出;現在很多面向對象語言(如Java 、C#)的運行環境都提供了自動垃圾回收的技術,因此,如果實例化的對象長時間不被利用,系統會認為它是垃圾,會自動銷毀並回收資源,下次利用時又將重新實例化,這將導致對象狀態的丟失。
5.10. 適用環境
在以下情況下可以使用單例模式:
- 系統只需要一個實例對象,如係統要求提供一個唯一的序列號生成器,或者需要考慮資源消耗太大而只允許創建一個對象。
- 客戶調用類的單個實例只允許使用一個公共訪問點,除了該公共訪問點,不能通過其他途徑訪問該實例。
- 在一個系統中要求一個類只有一個實例時才應當使用單例模式。反過來,如果一個類可以有幾個實例共存,就需要對單例模式進行改進,使之成為多例模式
5.11. 模式應用
一個具有自動編號主鍵的表可以有多個用戶同時使用,但數據庫中只能有一個地方分配下一個主鍵編號,否則會出現主鍵重複,因此該主鍵編號生成器必須具備唯一性,可以通過單例模式來實現。
5.12. 模式擴展
5.13. 總結
- 單例模式確保某一個類只有一個實例,而且自行實例化並向整個系統提供這個實例,這個類稱為單例類,它提供全局訪問的方法。單例模式的要點有三個:一是某個類只能有一個實例;二是它必須自行創建這個實例;三是它必須自行向整個系統提供這個實例。單例模式是一種對象創建型模式。
- 單例模式只包含一個單例角色:在單例類的內部實現只生成一個實例,同時它提供一個靜態的工廠方法,讓客戶可以使用它的唯一實例;為了防止在外部對其實例化,將其構造函數設計為私有。
- 單例模式的目的是保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。單例類擁有一個私有構造函數,確保用戶無法通過new關鍵字直接實例化它。除此之外,該模式中包含一個靜態私有成員變量與靜態公有的工廠方法。該工廠方法負責檢驗實例的存在性並實例化自己,然後存儲在靜態成員變量中,以確保只有一個實例被創建。
- 單例模式的主要優點在於提供了對唯一實例的受控訪問並可以節約系統資源;其主要缺點在於因為缺少抽象層而難以擴展,且單例類職責過重。
- 單例模式適用情況包括:系統只需要一個實例對象;客戶調用類的單個實例只允許使用一個公共訪問點。
留言
張貼留言