前言
單例模式是最簡單的一種模式。在Go中,單例模式指的是全局只有一個實例,并且它負責創建自己的對象。單例模式有減少內存和系統資源開銷、防止多個實例產生沖突等優點。
因為單例模式保證了實例的全局唯一性,并且只被初始化一次,所以比較適合全局共享一個實例,且只需要被初始化一次的場景,例如數據庫實例、全局配置、全局任務池等。
單例模式又分為餓漢方式和懶漢方式。餓漢方式是指全局的單例實例在包被加載時創建,而懶漢方式指全局的單例實例在第一次被使用時創建。其中懶漢方式是開源項目中使用最多的方式。
示例代碼
Go
懶漢方式的缺點是非并發安全,實際使用中一般加鎖,或者使用sync.Once
package singleton import "sync" type Singleton interface { foo()
} type singleton struct{} func (s singleton) foo() {} var ( instance *singleton once sync.Once
) func GetInstance() Singleton { once.Do(func() { instance = &singleton{} }) return instance
}
單元測試
package singleton import ( "sync" "testing") const parCount = 100 func TestSingleton(t *testing.T) { ins1 := GetInstance() ins2 := GetInstance() if ins1 != ins2 { t.Fatal("instance is not equal") }
} func TestParallelSingleton(t *testing.T) { start := make(chan struct{}) wg := sync.WaitGroup{} wg.Add(parCount) instance := [parCount]Singleton{} for i := 0; i < parCount; i++ { go func(index int) { <-start instance[index] = GetInstance() wg.Done() }(i) } close(start) wg.Wait() for i := 1; i < parCount; i++ { if instance[i] != instance[i-1] { t.Fatal("instance is not equal") } }
}
Python
python的包是天然的單例模式,只要放到單獨的包中,import時就是引用的單例。
如果要在一個包內使用設計模式,也有以下幾種方式。
使用函數裝飾器實現單例
def singleton(cls): _instance = {} def inner(): if cls not in _instance: _instance[cls] = cls() return _instance[cls] return inner @singleton
class MyCls: def __init__(self): pass if __name__ == "__main__": a = MyCls() b = MyCls() print(id(a) == id(b)) # 輸出結果應為 True
使用類裝飾器實現單例
class Singleton: def __init__(self, cls): self._cls = cls self._instance = {} def __call__(self): if self._cls not in self._instance: self._instance[self._cls] = self._cls() return self._instance[self._cls] @Singleton
class MyCls: def __init__(self): pass if __name__ == "__main__": a = MyCls() b = MyCls() print(id(a) == id(b)) # 輸出結果應該是True