設計模式之單例模式

落雷發表於2023-09-23

單例模式簡介

單例模式是一種設計模式,用於確保一個類只有一個例項,並提供全域性訪問點以獲取該例項。它是一種建立型模式,通常用於需要嚴格控制某個類的例項數量的情況。單例模式確保一個類在整個應用程式生命週期中只有一個例項,因此可以節省系統資源,同時提供了一個集中的訪問點,以便在需要時獲取該例項。

以下是單例模式的關鍵特點:

  1. 單一例項:單例模式確保一個類只有一個例項物件存在。
  2. 全域性訪問點:單例模式提供了一個全域性的訪問點,其他物件可以透過該訪問點獲取單例例項。
  3. 延遲載入(可選):在需要時才進行單例物件的建立,可以減少應用程式啟動時的資源佔用。
  4. 執行緒安全性(可選):在多執行緒環境下,單例模式需要考慮執行緒安全性,以確保只有一個例項被建立。

單例模式實現

懶漢模式(Lazy Initialization)和餓漢模式(Eager Initialization)是兩種單例模式的實現方式,它們之間的主要區別在於單例物件的初始化時機。

1. 懶漢模式(Lazy Initialization):

  • 初始化時機:懶漢模式是延遲載入的,也就是說,單例物件在首次訪問時才進行初始化。在多執行緒環境中,可能會出現競態條件,需要額外的執行緒安全措施來確保只建立一個例項。
  • 優點
    • 節省了系統資源,因為在應用程式啟動時不會建立單例物件。
    • 可以實現延遲載入,只有在需要時才進行初始化。
  • 缺點
    • 在多執行緒環境下,需要考慮執行緒安全性,通常需要使用互斥鎖等機制來保證單例物件的唯一性。
    • 首次訪問單例物件時可能會引入額外的效能開銷,因為需要進行初始化。

2. 餓漢模式(Eager Initialization):

  • 初始化時機:餓漢模式是在應用程式啟動時就進行單例物件的初始化,無論是否會被使用。因此,單例物件在應用程式生命週期內都存在。
  • 優點
    • 不需要考慮多執行緒環境下的執行緒安全性,因為單例物件在應用程式啟動時就已經建立。
    • 訪問單例物件時不會引入額外的效能開銷,因為它已經初始化。
  • 缺點
    • 可能會浪費系統資源,因為單例物件在應用程式啟動時就被建立,如果一直未被使用,可能會佔用記憶體。
    • 不支援延遲載入,因為單例物件在應用程式啟動時就已經初始化。

如何選擇懶漢模式還是餓漢模式:

  • 如果應用程式對資源要求敏感,希望儘量減少啟動時的記憶體佔用,或者需要支援延遲載入,可以選擇懶漢模式。
  • 如果應用程式對效能要求高,可以接受在應用程式啟動時進行初始化,並且不希望處理多執行緒環境下的執行緒安全問題,可以選擇餓漢模式。

總之,選擇懶漢模式還是餓漢模式應該根據具體的需求和效能要求來決定。無論選擇哪種模式,都需要確保單例物件的唯一性,以及在多執行緒環境下的執行緒安全性。

懶漢模式實現

在 Go 中實現懶漢模式相對簡單,因為 Go 的包系統和併發機制使得這一模式變得非常優雅和安全。下面是一個示例,展示瞭如何在 Go 中建立一個執行緒安全的單例物件:

package singleton

import (
	"sync"
)

// Singleton 是一個單例物件的結構體
type Singleton struct {
	data int
}

var instance *Singleton
var once sync.Once

// GetInstance 返回 Singleton 的唯一例項
func GetInstance() *Singleton {
	once.Do(func() {
		instance = &Singleton{} // 只會執行一次
	})
	return instance
}

// SetData 設定 Singleton 的資料
func (s *Singleton) SetData(data int) {
	s.data = data
}

// GetData 獲取 Singleton 的資料
func (s *Singleton) GetData() int {
	return s.data
}

在這個示例中,我們建立了一個 Singleton 結構體,它包含一個欄位 data 用於儲存單例物件的資料。我們使用 sync.Once 來確保 GetInstnace 函式只會被執行一次,從而保證單例物件只會被建立一次。

main 函式或其他地方,您可以這樣使用這個單例物件:

package main

import (
	"fmt"
	"singleton"
)

func main() {
	instance1 := singleton.GetInstance()
	instance1.SetData(42)

	instance2 := singleton.GetInstance()

	fmt.Println("Instance 1 data:", instance1.GetData())
	fmt.Println("Instance 2 data:", instance2.GetData())

	if instance1 == instance2 {
		fmt.Println("Both instances are the same")
	}
}

這個示例中,我們首先透過 GetInstance 函式獲取單例物件 instance1,然後設定其資料為 42。接著,我們再次獲取單例物件 instance2,並檢查兩個例項是否相同,從而驗證單例模式的實現。

使用 sync.Once 是 Go 中實現單例模式的推薦方法,因為它既能保證執行緒安全,又能保證懶載入(即只在第一次訪問時建立例項)。這樣可以確保在應用程式中只存在一個例項,並且在需要時進行初始化。

餓漢模式實現

餓漢模式是在應用程式啟動時就進行單例物件的初始化。以下是一個使用餓漢模式的示例:

package singleton

type Singleton struct {
    data int
}

var instance = &Singleton{}

func GetInstance() *Singleton {
    return instance
}

func (s *Singleton) SetData(data int) {
    s.data = data
}

func (s *Singleton) GetData() int {
    return s.data
}

在這個示例中,我們在包級別直接建立了一個單例例項 instance,並在程式啟動時進行初始化。這意味著單例物件在應用程式啟動時就已經存在,而不是在首次訪問時才建立。


孟斯特

宣告:本作品採用署名-非商業性使用-相同方式共享 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意


相關文章