📚 單例模式概述

什麼是單例模式?

單例模式是一種創建型設計模式,確保一個類只有一個實例,並提供一個全局訪問點。

適用場景

  • 數據庫連接池
  • 配置管理器
  • 日誌記錄器
  • 緩存系統
  • 線程池

🏗️ 基礎單例模式實現

1. 非併發安全的基礎實現

package singleton

// 單例結構體
type Singleton struct {
    value string
}

// 包級私有實例
var instance *Singleton

// 獲取單例實例(非線程安全)
func GetInstance() *Singleton {
    if instance == nil {
        instance = &Singleton{
            value: "initial value",
        }
    }
    return instance
}

// 業務方法示例
func (s *Singleton) GetValue() string {
    return s.value
}

func (s *Singleton) SetValue(value string) {
    s.value = value
}

問題分析:

  • 在併發環境下,多個 goroutine 可能同時檢查到 instance == nil
  • 導致創建多個實例,違反單例原則

🔒 併發安全單例模式實現

2. 互斥鎖實現(懶漢式)

package singleton

import (
    "fmt"
    "sync"
)

var (
    instance *Singleton
    once     sync.Once
    mu       sync.Mutex
)

// 方法1:使用 sync.Mutex(雙重檢查鎖定)
func GetInstanceMutex() *Singleton {
    if instance == nil { // 第一次檢查,避免不必要的鎖競爭
        mu.Lock()
        defer mu.Unlock()
        
        if instance == nil { // 第二次檢查,確保實例只創建一次
            instance = &Singleton{
                value: "mutex instance",
            }
            fmt.Println("Singleton instance created with mutex")
        }
    }
    return instance
}

3. sync.Once 實現(推薦)

// 方法2:使用 sync.Once(Go語言推薦方式)
func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{
            value: "sync.Once instance",
        }
        fmt.Println("Singleton instance created with sync.Once")
    })
    return instance
}

📊 不同實現方式對比

性能測試代碼

package singleton

import (
    "sync"
    "testing"
)

// 基準測試:互斥鎖實現
func BenchmarkMutexSingleton(b *testing.B) {
    var wg sync.WaitGroup
    for i := 0; i < b.N; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            _ = GetInstanceMutex()
        }()
    }
    wg.Wait()
}

// 基準測試:sync.Once 實現
func BenchmarkOnceSingleton(b *testing.B) {
    var wg sync.WaitGroup
    for i := 0; i < b.N; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            _ = GetInstance()
        }()
    }
    wg.Wait()
}

性能對比結果

實現方式

併發安全

性能

代碼簡潔性

推薦度

基礎實現

❌ 不安全

⭐⭐⭐⭐⭐

⭐⭐⭐⭐⭐

❌ 不推薦

互斥鎖

✅ 安全

⭐⭐⭐

⭐⭐⭐

⭐⭐⭐

sync.Once

✅ 安全

⭐⭐⭐⭐⭐

⭐⭐⭐⭐⭐

⭐⭐⭐⭐⭐

🎯 完整的實戰示例

配置管理器單例

package config

import (
    "encoding/json"
    "os"
    "sync"
)

// Config 配置結構體
type Config struct {
    Database DatabaseConfig `json:"database"`
    Server   ServerConfig   `json:"server"`
    Redis    RedisConfig    `json:"redis"`
}

type DatabaseConfig struct {
    Host     string `json:"host"`
    Port     int    `json:"port"`
    Username string `json:"username"`
    Password string `json:"password"`
    DBName   string `json:"dbname"`
}

type ServerConfig struct {
    Port         int    `json:"port"`
    ReadTimeout  int    `json:"read_timeout"`
    WriteTimeout int    `json:"write_timeout"`
}

type RedisConfig struct {
    Addr     string `json:"addr"`
    Password string `json:"password"`
    DB       int    `json:"db"`
}

var (
    configInstance *Config
    configOnce     sync.Once
    configMu       sync.RWMutex
)

// GetConfig 獲取配置單例
func GetConfig() *Config {
    configOnce.Do(func() {
        // 從配置文件加載配置
        configInstance = loadConfig()
    })
    return configInstance
}

// 線程安全的配置獲取方法
func (c *Config) GetDatabaseConfig() DatabaseConfig {
    configMu.RLock()
    defer configMu.RUnlock()
    return c.Database
}

// 線程安全的配置更新方法
func (c *Config) UpdateDatabaseConfig(newConfig DatabaseConfig) {
    configMu.Lock()
    defer configMu.Unlock()
    c.Database = newConfig
}

// 加載配置文件
func loadConfig() *Config {
    file, err := os.Open("config.json")
    if err != nil {
        // 返回默認配置
        return &Config{
            Database: DatabaseConfig{
                Host:     "localhost",
                Port:     5432,
                Username: "admin",
                Password: "password",
                DBName:   "myapp",
            },
            Server: ServerConfig{
                Port:         8080,
                ReadTimeout:  30,
                WriteTimeout: 30,
            },
            Redis: RedisConfig{
                Addr:     "localhost:6379",
                Password: "",
                DB:       0,
            },
        }
    }
    defer file.Close()

    var config Config
    decoder := json.NewDecoder(file)
    if err := decoder.Decode(&config); err != nil {
        panic(err)
    }

    return &config
}

數據庫連接池單例

package database

import (
    "database/sql"
    "fmt"
    "sync"
    _ "github.com/lib/pq"
)

type DatabaseManager struct {
    db   *sql.DB
    mu   sync.RWMutex
}

var (
    dbInstance *DatabaseManager
    dbOnce     sync.Once
)

func GetDBManager() *DatabaseManager {
    dbOnce.Do(func() {
        db, err := sql.Open("postgres", 
            "host=localhost port=5432 user=admin password=secret dbname=myapp sslmode=disable")
        if err != nil {
            panic(fmt.Sprintf("Failed to connect to database: %v", err))
        }
        
        // 測試連接
        if err = db.Ping(); err != nil {
            panic(fmt.Sprintf("Failed to ping database: %v", err))
        }
        
        dbInstance = &DatabaseManager{db: db}
        fmt.Println("Database connection pool initialized")
    })
    return dbInstance
}

// 線程安全的查詢方法
func (dm *DatabaseManager) Query(query string, args ...interface{}) (*sql.Rows, error) {
    dm.mu.RLock()
    defer dm.mu.RUnlock()
    return dm.db.Query(query, args...)
}

// 線程安全的執行方法
func (dm *DatabaseManager) Exec(query string, args ...interface{}) (sql.Result, error) {
    dm.mu.Lock()
    defer dm.mu.Unlock()
    return dm.db.Exec(query, args...)
}

// 關閉數據庫連接
func (dm *DatabaseManager) Close() error {
    dm.mu.Lock()
    defer dm.mu.Unlock()
    return dm.db.Close()
}

🧪 單元測試

package singleton_test

import (
    "singleton"
    "sync"
    "testing"
)

// 測試單例實例的唯一性
func TestSingletonUniqueness(t *testing.T) {
    var wg sync.WaitGroup
    instances := make([]*singleton.Singleton, 100)
    
    for i := 0; i < 100; i++ {
        wg.Add(1)
        go func(index int) {
            defer wg.Done()
            instances[index] = singleton.GetInstance()
        }(i)
    }
    wg.Wait()
    
    // 驗證所有實例都是同一個
    firstInstance := instances[0]
    for i, instance := range instances {
        if instance != firstInstance {
            t.Errorf("Instance at index %d is not the same as first instance", i)
        }
    }
}

// 測試併發安全性
func TestSingletonConcurrent(t *testing.T) {
    const goroutines = 1000
    var wg sync.WaitGroup
    wg.Add(goroutines)
    
    for i := 0; i < goroutines; i++ {
        go func() {
            defer wg.Done()
            instance := singleton.GetInstance()
            instance.SetValue("test")
            _ = instance.GetValue()
        }()
    }
    wg.Wait()
}

🚀 最佳實踐總結

1. 首選 sync.Once

var (
    instance *Singleton
    once     sync.Once
)

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}

2. 避免全局變量污染

// 不好的做法:導出實例變量
var Instance *Singleton

// 好的做法:通過函數訪問
func GetInstance() *Singleton {
    // ...
}

3. 考慮延遲初始化

type LazySingleton struct {
    data []string
}

func (s *LazySingleton) loadData() {
    // 延遲加載昂貴資源
    if s.data == nil {
        s.data = loadFromDatabase()
    }
}

4. 處理初始化錯誤

var (
    instance *Singleton
    once     sync.Once
    initErr  error
)

func GetInstance() (*Singleton, error) {
    once.Do(func() {
        instance, initErr = initializeSingleton()
    })
    return instance, initErr
}

func initializeSingleton() (*Singleton, error) {
    // 初始化邏輯,可能返回錯誤
    if err := someInitialization(); err != nil {
        return nil, err
    }
    return &Singleton{}, nil
}

⚠️ 注意事項

  1. 避免過度使用單例 - 單例模式可能引入全局狀態,增加代碼耦合度
  2. 考慮依賴注入 - 在大型項目中,考慮使用依賴注入替代單例
  3. 測試困難 - 單例可能使單元測試變得複雜
  4. 生命週期管理 - 注意單例的初始化和清理時機

📝 總結

Go 語言中實現單例模式的最佳實踐是使用 sync.Once,它提供了:

  • ✅ 完美的併發安全性
  • ✅ 優秀的性能表現
  • ✅ 簡潔的代碼實現
  • ✅ 內置的線程安全保證