編程旅途是漫長遙遠的,在不同時刻有不同的感悟,本文會一直更新下去,
思考總結
什么是單例模式
單例模式(Singleton Pattern)屬于創建型模式,它提供了一種創建物件的最佳方式,
單例模式:保證一個類僅有一個實體,并提供一個訪問它的全域訪問點,
含義:
-
這種模式涉及到一個單一的類,該類負責創建自己的物件,同時確保只有單個物件被創建,這個類提供了一種訪問其唯一的物件的方式,可以直接訪問,不需要實體化該類的物件,
-
職責角度看,實體化與否不應該由使用方判斷,而是應該由自己來判斷,將實體化判斷程序遷移到
GetInstance()函式, -
Singleton類封裝它的唯一實體,這樣它可以嚴格地控制客戶怎樣訪問它以及何時訪問它,簡單地說就是對唯一實體的受控訪問,我們可以更改
GetInstance()函式添加條件和引數實作受控訪問,
注意:
- 單例類必須自己創建自己的唯一實體,
- 單例類必須給所有其他物件提供這一實體,
- 通常情況下,單例類只能有一個實體,
- 在特殊情況下,你可以隨時調整限制并設定生成單例實體的數量, 只需修改獲取實體方法, 即 getInstance 中的代碼即可實作,
實作方式:
-
將默認建構式設為私有, 防止其他物件使用單例類的 new 運算子,
-
新建一個靜態構建方法作為建構式,該函式會“偷偷”呼叫私有建構式來創建物件,并將其保存在一個靜態成員變數中,此后所有對于該函式的呼叫都將回傳這一快取物件(判斷系統是否已經有這個單例,如果有則回傳,如果沒有則創建)
-
檢查客戶端代碼,將對單例的建構式的呼叫替換為對其靜態構建方法的呼叫,
-
需要私有靜態成員變數保存單例、公有靜態構建方法獲取單例、類的建構式私有化保護單例實體,
應用實體:
- 1、一個班級只有一個班主任,
- 2、Windows 是多行程多執行緒的,在操作一個檔案的時候,就不可避免地出現多個行程或執行緒同時操作一個檔案的現象,所以所有檔案的處理必須通過唯一的實體來進行,
- 3、一些設備管理器常常設計為單例模式,比如一個電腦有兩臺列印機,在輸出的時候就要處理不能兩臺列印機列印同一個檔案,
優點:
- 1、在記憶體里只有一個實體,減少了記憶體的開銷,尤其是頻繁的創建和銷毀實體(比如管理學院首頁頁面快取),
- 2、避免對資源的多重占用(比如寫檔案操作),
缺點:
- 違反了單一職責原則,該模式同時解決了兩個問題,
- 單例模式可能掩蓋不良設計,比如程式各組件之間相互了解過多等,
- 該模式在多執行緒環境下需要進行特殊處理,避免多個執行緒多次創建單例物件,
- getInstance() 方法中需要使用同步鎖防止多執行緒同時進入造成 instance 被多次實體化,
- 單例的客戶端代碼單元測驗可能會比較困難,因為許多測驗框架以基于繼承的方式創建模擬物件,由于單例類的建構式是私有的,而且絕大部分語言無法重寫靜態方法,所以你需要想出仔細考慮模擬單例的方法,要么干脆不撰寫測驗代碼,或者不使用單例模式,
使用場景:
- 1、要求生產唯一序列號,
- 2、WEB 中的計數器,不用每次重繪都在資料庫里加一次,用單例先快取起來,
- 3、創建的一個物件需要消耗的資源過多,比如 I/O 與資料庫的連接等,
與其他模式的關系:
- 外觀類通常可以轉換為單例類,因為在大部分情況下一個外觀物件就足夠了,
- 如果你能將物件的所有共享狀態簡化為一個享元物件,那么享元就和單例類似了,但這兩個模式有兩個根本性的不同,
- 只會有一個單例物體,但是享元類可以有多個物體,各物體的內在狀態也可以不同,
- 單例物件可以是可變的,享元物件是不可變的,
- 抽象工廠、生成器和原型都可以用單例來實作,
雙重”鎖定“
原子操作,是并發編程中”最小的且不可并行化“的操作,本案例中使用原子操作配合互斥鎖實作了非常高效的單例模式,互斥鎖的代價比普通整數的原子讀寫高很多,所以在性能敏感的地方添加一個initialized標志位,通過原子檢測標志位狀態降低互斥鎖的次數來提高性能,(也可以使用實體來進行判斷,在實體未被創建的時候再加鎖處理)
在互斥鎖之后還要判斷實體是否存在,是因為當多執行緒的時候,當一個執行緒處理完退出解鎖,另一個在排隊等候的執行緒進入后如果沒有實體的判斷,那么會再生成一遍實體,沒有達到單例的目的,
靜態初始化
C# 提供了靜態初始化的方法,這種方法可以解決多執行緒環境下不安全的原因,
C# 給類添加sealed關鍵字防止子類繼承產生多個單例、給靜態欄位添加readonly修改為只讀狀態,意味著只能在靜態初始化期間或在類建構式中分配變數,
這種靜態初始化的方式是在自己被加載時就將自己實體化,所以被形象地稱之為餓漢式單例類,原先的單例模式處理方式是要在被第一次參考時才會自己實體化,這叫懶漢式初始化,
餓漢式初始化是類一加載就實體化的物件,所以要提前占用系統資源,然而懶漢式,又會面臨多執行緒訪問的安全性問題,需要做雙重鎖定才能保證安全,
在golang實作靜態初始化,實際上只要把實體化的程序移動到當前檔案的init函式中,在包被加載的程序中,會首先運行各個檔案的init函式,再運行main函式,
實用類與單例類的比較
在C#經常有工具類之說,這個工具類包含許多靜態方法和靜態屬性,但是這種實用類沒有單例類的狀態,實用類不能用于繼承多臺,而單例類雖然實體唯一,但是可以有子類來繼承,實用類是一些方法屬性的集合,單例是有著唯一物件的實體,
程式介紹

本程式實作了單例模式,三個作業者需要各自找到電梯搭乘!電梯只有一個!
PS C:\Users\小能喵喵喵\Desktop\設計模式\單例模式> go run .
向海寧 正在搭乘電梯!
田海彬 正在搭乘電梯!
程式代碼
singleton.go
package main
import (
"fmt"
"sync"
"sync/atomic"
)
type People string
type Elevator struct {
passengers map[People]bool
}
var (
elevator *Elevator
initialized uint32
mu sync.Mutex
)
// 餓漢式單例
func init() {
initialized = 1
elevator = &Elevator{make(map[People]bool)}
}
// 乘客進電梯
func (p People) intoElevator() {
e := ElevatorGetInstance()
e.passengers[p] = true
}
// 乘客出電梯
func (p People) outElevator() {
e := ElevatorGetInstance()
delete(e.passengers, p)
}
// 乘客按下電梯樓層按鈕
func (p People) pressButton() {
e := ElevatorGetInstance()
e.start()
}
// 電梯開始運作
func (e *Elevator) start() {
for k := range e.passengers {
fmt.Println(k, "正在搭乘電梯!")
}
}
// 由于Golang不支持靜態方法、靜態欄位,所以使用純函式替代面向物件中的靜態方法
// 乘客想知道電梯在哪,單例模式
func ElevatorGetInstance() *Elevator {
if atomic.LoadUint32(&initialized) == 1 {
return elevator
}
mu.Lock()
defer mu.Unlock()
// ^ 懶漢式單例
if elevator == nil {
elevator = &Elevator{make(map[People]bool)}
atomic.StoreUint32(&initialized, 1)
}
return elevator
}
main.go
package main
// 單例模式
// by 小能喵喵喵 2022年9月8日
func main() {
var workerA, workerB, workerC People = "陳冰", "向海寧", "田海彬"
workerA.intoElevator()
workerB.intoElevator()
workerC.intoElevator()
// workerA 發現自己電梯坐錯了
workerA.outElevator()
// workerB 按下了電梯按鈕
workerB.pressButton()
}
Console
PS C:\Users\小能喵喵喵\Desktop\設計模式\單例模式> go run .
向海寧 正在搭乘電梯!
田海彬 正在搭乘電梯!
補充C#單執行緒單例實作
class Singleton
{
private static Singleton instance;
private Singleton() //使用 private 欄位外界無法使用new手動創建實體,只能通過靜態方法創建
{
}
public static Singleton GetInstance()
{
if(instance == null)
{
instance = new Singleton();
}
return instance
}
}
所有類都有構造方法,不編碼則系統默認生成空的構造方法,若有顯示定義的構造方法,默認的構造方法就會失效,
參考資料
- 《Go語言核心編程》李文塔
- 《Go語言高級編程》柴樹彬、曹春輝
- 《大話設計模式》程杰
- 《深入設計模式》亞歷山大·什韋茨
- 單例模式 | 菜鳥教程
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/509016.html
標籤:其他
