定義
單例模式是一種常用的軟件設計模式。在它的核心結構中只包含一個被稱爲單例類的特殊類。通過單例模式可以保證系統中一個類只有一個實例而且該實例易於外界訪問,從而方便對實例個數的控制並節約系統資源。如果希望在系統中某個類的對象只能存在一個,單例模式是最好的解決方案。
需求分析
對於系統中的某些類來說,只有一個實例很重要,例如,一個系統中可以存在多個打印任務,但是只能有一個正在工作的任務;一個系統只能有一個窗口管理器或文件系統;一個系統只能有一個計時工具或ID(序號)生成器。如在Windows中就只能打開一個任務管理器。如果不使用機制對窗口對象進行唯一化,將彈出多個窗口,如果這些窗口顯示的內容完全一致,則是重複對象,浪費內存資源;如果這些窗口顯示的內容不一致,則意味着在某一瞬間系統有多個狀態,與實際不符,也會給用戶帶來誤解,不知道哪一個纔是真實的狀態。因此有時確保系統中某個對象的唯一性即一個類只能有一個實例非常重要。
就如上面所提到的打印機,一臺打印機一個時刻只有一個進程(實例)在運行,否則打印機會出現錯亂。
解決方案
如何保證一個類只有一個實例並且這個實例易於被訪問呢?定義一個全局變量可以確保對象隨時都可以被訪問,但不能防止我們實例化多個對象。一個更好的解決辦法是讓類自身負責保存它的唯一實例。這個類可以保證沒有其他實例被創建,並且它可以提供一個訪問該實例的方法。這就是單例模式的模式動機。
那麼怎麼讓類自身負責保存它的唯一實例?Go有一個優雅的解決方案就是標準庫裏的sync.Once
。它是用來判斷某個函數是否已經執行過一次,如果沒有就執行,如果執行過就不會再執行這個函數。
package singleton
import (
"fmt"
"sync"
)
// 首字母小寫,防止外部使用,私自實例化
type printer struct{
Counter int // 計數器,計算是第幾次打印
Buf string // 要打印的內容的緩存
}
var (
once sync.Once // 來判斷某個函數是否已經執行過一次,如果沒有就執行,如果執行過就不會再執行
instance *printer // 實例
)
func (p *printer) MyPrint() {
if len(p.Buf) != 0 { // 只有緩存有東西時纔打印內容
p.Counter++
fmt.Println(p.Counter, ".Printer is printing:", p.Buf)
p.Buf = "" // 打印完緩存置空
}
}
func (p *printer) SetContent(s string) {
p.Buf = s
}
func NewPrinter() *printer {
once.Do(func() {
instance = &printer{0, ""}
})
return instance
}
使用方式:
package main
import (
"fmt"
"studygo/pattern/singleton"
)
func main() {
printer := singleton.NewPrinter() // 第一個實例
printer.SetContent("hello")
printer.MyPrint()
printer1 := singleton.NewPrinter() // 第二個實例
printer1.SetContent("Hi")
printer1.MyPrint()
// 由於NewPrinter返回的是指針,所以我們可以簡單的看下兩個打印機地址是否相同
if printer1 == printer {
fmt.Println("They are the same printer")
}
}
結果:
1 .Printer is printing: hello
2 .Printer is printing: Hi
They are the same printer
從結果來看printer
和printer1
是一樣的對象。可以從兩個地方驗證,結果前面的字母,因爲打印機每次打印都會進行計數,如果重新生成一個打印機,那麼計數就會從0開始,說明這兩個打印機是同一個實例。另外生成的實例對象是一個指針,可以比較指針來判斷來個實例的地址是否一樣~!
參考文獻:https://books.studygolang.com/go-patterns/
撩我?
搜索我的微信公衆中號:Kyda