[TOC]
1、單例模式概述
單例模式(Singleton
)[GOF95
]是一種對象的創建模式,確保系統中使用了單例模式的類型只會存在一個實例對象,通過該對象給系統提供一致性的解決方案。
單例模式管理的類型,常規情況下具備如下特性
- 單例類只能有一個實例
- 單例類必須自己創建屬于自己的唯一實例對象。
- 單例類必須給其他調用者提供自己的實例對象。
注意:在項目開發過程中,項目可能會存在需要限定多個實例的情況,所以單例模式是一個基礎模式,可以在此模式基礎上衍生
多例模式
。
單例實現過程中,根據實例的創建時機區分為餓漢式單例
和 懶漢式單例
兩種不同的操作方式,所謂餓漢式
表示在類型定義時在類型中已經創建了對應的實例,其他調用者可以隨時調用獲取;所謂懶漢式
是在類型定義時定義了單例的實現邏輯,但是只有調用者在使用該單例時,第一次調用時才會發生具體實例化過程,之后再使用過程中該實例就是唯一的提供服務的對象。
1.1、 核心組件
單例模式中,最核心的部分就是當前類型自己,為了符合需求中對于單例的使用要求,通常情況下我們會設計當前類型自己的靜態方法來獲取該類型的實例,并且當前類型不可實例化(構造方法私有化)。
- 單例類(Singleton Class):單例模式的核心類型,和系統中的業務邏輯緊密關聯,實例的創建部分的處理由自身的靜態方法完成,對外只暴露包含業務功能的單個實例對象。
1.2、 優點缺陷
單例類的設計通常是基于某些系統功能的要求,一般情況下是基于功能上的限制或者業務上的要求,某一部分功能業務邏輯在整個系統平臺的運行周期中,必須是讓所有功能模塊共享的獨立一份,此時單例模式的應用能簡單開發過程中的業務邏輯的復雜度,但是注意單例模式并不是唯一的解決方案。
優點
- 能有好的處理系統中獨立一份數據的共享和信息傳遞
- 減少內存開銷,避免資源過度占用
缺點
- 對于
DIP原則
以及OCP原則
支持不友好,功能擴展不方便
2、 Java
實現
2.1、餓漢式單例模式
餓漢式單例模式基礎代碼,如有功能或者業務邏輯上的需要,可以在此基礎上進行擴展。
/**
* <p>項目文檔: TODO</p>
*
* @author 大牧
* @version V1.0
*/
public class SingletonForHungry {
// 1. 內部屬性中直接實例化當前對象
private static SingletonForHungry myInstance = new SingletonForHungry();
// 2. 構造方法私有化,屏蔽外界實例化途徑
private SingletonForHungry () {}
// 3. 提供獲取實例對象的靜態方法
public static SingletonForHungry getInstance() {
return myInstance;
}
}
測試代碼,請獨立創建一個入口類進行測試
/**
* <p>項目文檔: TODO</p>
*
* @author 大牧
* @version V1.0
*/
public class SingletonMain {
public static void main(String[] args) {
// 測試代碼
SingletonForHungry sf = SingletonForHungry.getInstance();
System.out.println(sf); // SingletonForHungry@61bbe9ba
SingletonForHungry sf2 = SingletonForHungry.getInstance();
System.out.println(sf2); // SingletonForHungry@61bbe9ba
// SingletonForHungry sf3 = new SingletonForHungry();// ERROR
// System.out.println(sf3);
}
}
2.2、 懶漢式單例模式
/**
* <p>項目文檔: 懶漢式單例模式</p>
*
* @author 大牧
* @version V1.0
*/
public class SingletonForLazy {
// 1. 聲明一個存放實例對象的變量
private static SingletonForLazy myInstance;
// 2. 構造方法私有化
private SingletonForLazy() {}
// 3. 提供獲取當前類型實例對象的靜態方法
public static SingletonForLazy getInstance() {
myInstance = myInstance == null ? new SingletonForLazy():myInstance;
return myInstance;
}
}
測試代碼:
/**
* <p>項目文檔: TODO</p>
*
* @author 大牧
* @version V1.0
*/
public class SingletonMain {
public static void main(String[] args) {
// 懶漢式單例 測試代碼
SingletonForLazy sl = SingletonForLazy.getInstance();
System.out.println(sl); // SingletonForLazy@61bbe9ba
SingletonForLazy sl2 = SingletonForLazy.getInstance();
System.out.println(sl2); // SingletonForLazy@61bbe9ba
// SingletonForLazy sl3 = new SingletonForLazy();// ERROR
// System.out.println(sl3);
}
}
注意:這里的單例模式,主要是業務上的限制,并不代表在代碼執行過程中外界不能干預,比如通過反射的方式實例化單例類的其他實例,但是根本上來說只要符合需求的模式下,單例模式能解決我們在項目中遇到的通用問題,也就是單例模式就是一種解決方案,會因不同的場景而進行不同的設計。
3、 Python
實現
Python
本身是弱類型語言,所以單例模式的實現手段較多,我們這里簡單給大家展示幾種操作方式。
3.1、 單實例操作
Python
中的一切都是數據都是對象,包括類型的聲明定義也是一種對象,在操作過程中根據這一特性我們可以直接構建單例模式
singleton.py
-------------------------------------------
"""
單例模式
"""
# 實現1:單實例操作
class Singleton:
pass
# 創建實例對象
singleton_instalce = Singleton()
# 刪除類定義
del Singleton
print(singleton_instalce) # <__main__.Singleton object at 0x10c221080>
singleton_instalce2 = Singleton() # NameError: name 'Singleoton' is not defined
print(singleton_instalce2)
上述代碼中,通過類型Singleton
可以很方便的創建屬于該類型的對象singleton_instance
,完成對象創建后刪除了類型的定義,在后續的代碼使用過程中,利用Python
語言本身的特性,可以通過import
方式使用該單例對象,其他Python
模塊中可以按照如下方式引入使用
# 引入單例實例
from singleton import singleton_instance
3.2、 靜態方法
通過類型的靜態方法,提供一個獲取當前類型實例化的公共API,在業務上限制該實例的單例操作模式
# 實現2:靜態方法
import threading
class Singleton:
_instance_lock = threading.Lock()
@classmethod
def get_instance(self, *args, **kwargs):
"""獲取當前實例的靜態方法"""
if not hasattr(Singleton, "_my_instance"):
# 線程鎖,保證多線程訪問安全
with Singleton._instance_lock:
Singleton._my_instance = Singleton()
return Singleton._my_instance
singleton = Singleton.get_instance()
print(singleton) #<__main__.Singleton object at 0x102dc00f0>
singleton2 = Singleton.get_instance()
print(singleton2) #<__main__.Singleton object at 0x102dc00f0>
但是上述的單例操作中,不排除開發人員可能會通過類型直接構建對象的行為,代碼如下:
singleton3 = Singleton()
print(singleton3) #<__main__.Singleton object at 0x1012c00b8>
所以我們在這里操作的單例模式,描述為添加了業務限制的單例操作,在業務要求中如果要使用該類型的實例必須通過get_instance()
方法進行獲取。
3.3、 __new__
魔法方法
Python
中的類型在實例化過程中,逐次調用了__new__(cls)
進行實例的構建和__init__(self)
進行數據的初始化操作,我們通過__new__(cls)
魔法方法直接完成單例的處理,是Python
中最常見的一種操作方式。
# 實現3:__new__()魔法方法
class Singleton:
import threading
_instance_lock = threading.Lock()
def __init__(self, name):
self.name = name
def __new__(cls, *args, **kwargs):
"""構建方法"""
if not hasattr(Singleton, "_instance"):
with Singleton._instance_lock:
Singleton._instance = object.__new__(cls)
return Singleton._instance
# 分別創建兩個不同的實例
singleton = Singleton("tom")
singleton2 = Singleton("jerry")
# 查看兩個不同實例的屬性數據和內存地址
print(singleton, singleton.name) # <__main__.Singleton object at 0x101d13128> jerry
print(singleton2, singleton2.name) # <__main__.Singleton object at 0x101d13128> jerry
通過上述代碼可以看到,不同時間創建的對象,最終的數據都是一致的,包括在內存中的地址,也就是表明了當前的多個實例是同一個內存中的的對象數據,因為添加了線程鎖,所以該單例模式即使在多線程環境下也是安全的。
3.4、 裝飾器實現
上述的幾種實現方式都非常有好,尤其是通過__new__(cls)
的實現操作,但是在實際開發過程中代碼的復用性并不是非常好,我們可以使用Python
中的裝飾器完成單例模式的檢查約束,同時也保證了代碼的復用性。但是裝飾器的操作模式并不是全部適用,如果一個系統中出現大量的單例對象(當然基本不可能出現),裝飾器的操作模式就會特別消耗系統資源,廢話不多直接看實現
# 實現4:裝飾器操作
def singleton(cls):
"""單例類裝飾器"""
# 聲明一個存儲對象的字典
_instance = dict()
def _singleton(*args, **kwargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kwargs)
return _instance
return _singleton
@singleton
class Single:
def __init__(self, name):
self.name = name
s1 = Single("tom")
s2 = Single("jerry")
print(s1) # <class '__main__.Single'>:<__main__.Single object at 0x10b3b10b8>
print(s2) # <class '__main__.Single'>:<__main__.Single object at 0x10b3b10b8>
由于Python
語言本身的靈活性,基于python
的實現方式比較多,大家可以在實現過程中自行參考。
4、 Go
實現
Go
語言是通過結構體來定義類型的,所以單例的操作就比較靈活了,這里介紹一種簡單的實現操作模式
package main
import (
"fmt"
"sync"
)
type Singleton struct {}
var instance *Singleton
var once sync.Once
func GetInstance() *Singleton {
once.Do(func() {
fmt.Println("instance invoking....")
instance = &Singleton{}
})
return instance
}