Singleton Pattern,致獨一無二的你
Singleton Pattern,致獨一無二的你

Singleton Pattern,致獨一無二的你

Tags
Golang
Hey! Go Design Patterns
ithome 2021 ironman
Date
Sep 18, 2021

什麼是 Singleton Pattern?

notion image
整個程式運作只會有此一個物件,不會創建第二個重複的物件
在實際情境中某些物件,比如連線 DB、與其他 service 長連線、取得環境 config,我們都希望可以整個運行環境「只有一個」,才不會造成資源的浪費,或者系統每個部分運行得到的值不同。

問題情境

取得連線 DB 的物件,並且整個系統不會重複創建導致浪費 DB 連線的資源。

解決方式

相關的 code 在Github - go-design-patterns
第一種方式(初始化即創建):在 import 模組的時候,將 db 透過init()創建dbInstance{}(init()function 有著程式 runtime 會先行運行的特性),此後只有GetInstance()這個公開方法可以取得dbInstance{},並無任何方法再創建dbInstance{}
優點:可以在初始化就創建 instance,不用等到呼叫才創建(第二種方式) 缺點:無法控制初始化的時間點,舉例來說有可能「環境變數」套件都還沒有初始化,db 就開始初始化了,導致讀不到正確的環境變數
// db/db.go package db type DBInstance struct{} var dbInstance *DBInstance func init() { dbInstance = &DBInstance{} } func GetInstance() *DBInstance { return dbInstance }
// main file package main import "DAY-18/db" func main() { db.GetInstance() }
為了解決缺點,所以許多專案會不以init()來初始化物件,而是任意一個公開方法來初始化:
// db-2/db-2.go file package db type DBInstance struct{} var dbInstance *DBInstance func InitDB() { dbInstance = &DBInstance{} } func GetInstance() *DBInstance { return dbInstance }
// main file package main import db "DAY-18/db-2" func main() { db.Init() db.GetInstance() }
第二種方式(lazy 方式):在呼叫GetInstance()的時候會做檢查,如果dbInstance{}不存在就創建,存在就回傳現存的dbInstance{}
優點:可以在真的用到此物件時,才創建此物件,以節省記憶體,這即是 lazy 方式的優點 缺點:由於在使用物件時才會創建,所以第一次呼叫時需要創建會花較多的時間
package main type DBInstance struct{} var dbInstance *DBInstance func GetInstance() *DBInstance { if dbInstance == nil { dbInstance = &DBInstance{} } return dbInstance } func main() { GetInstance() }
如果覺得if dbInstance == nil的檢查很麻煩的話,也可以用sync.Once{}來實作,sync.Once{}.Do(func)可以讓 function 裡的物件永遠只執行一次,以達到 Singleton 的效果,裡頭適用 CAS 演算法來實作:
package main import "sync" type DBInstance struct{} var ( dbInstance *DBInstance once = &sync.Once{} ) func GetInstance() *DBInstance { once.Do(func() { dbInstance = &DBInstance{} }) return dbInstance } func main() { GetInstance() }