觀察者模式是一種行為型設計模式。這種模式允許一個實例(可以稱為目標對象)發布各種事件(event)給其他實例(觀察者)。這些觀察者會對目標對象進行訂閱,這樣每當目標對象發生變化時,觀察者就會收到事件(event)通知。
來看個例子:在電商網站經常會有各種商品脫銷,假如有顧客已經在關注這些商品,這些商品的脫銷就會對他們產生不好的體驗。如果顧客還想買這些商品,那么通常有如下解決方案:
- 顧客以一定的頻率檢查這些商品是否在售
- 電商平台將所有的上架的商品信息定期推送給用戶
- 顧客只訂閱他所關注的特定商品的信息,當這些商品再次上架時他們會收到通知;多個顧客可以訂閱同一個商品的信息
選項3是最為可行的一種方案。這也正是觀察者模式所能做到的事情。觀察者模式的核心組件為:
- Subject : 目標對象,是有變化發生時就會發布相關事件的實例
- Observer : 觀察者,訂閱目標對象的信息,會收到一些特定事件的通知
通常,Subject和Observer會被定義為接口,真正使用的是它們二者的具體實現。
UML類圖如下:

下面是一個示例代碼:
observer.go:
type observer interface {
update(string)
getID() string
}
subject.go:
type subject interface {
register(Observer observer)
deregister(Observer observer)
notifyAll()
}
item.go:
import "fmt"
type item struct {
observerList []observer
name string
inStock bool
}
func newItem(name string) *item {
return &item{
name: name,
}
}
func (i *item) updateAvailability() {
fmt.Printf("Item %s is now in stock\n", i.name)
i.inStock = true
i.notifyAll()
}
func (i *item) register(o observer) {
i.observerList = append(i.observerList, o)
}
func (i *item) deregister(o observer) {
i.observerList = removeFromSlice(i.observerList, o)
}
func (i *item) notifyAll() {
for _, observer := range i.observerList {
observer.update(i.name)
}
}
func removeFromSlice(observerList []observer, observerToRemove observer) []observer {
observerListLength := len(observerList)
for i, observer := range observerList {
if observerToRemove.getID() == observer.getID() {
observerList[observerListLength-1], observerList[i] = observerList[i], observerList[observerListLength-1]
return observerList[:observerListLength-1]
}
}
return observerList
}
customer.go:
import "fmt"
type customer struct {
id string
}
func (c *customer) update(itemName string) {
fmt.Printf("Sending email to customer %s for item %s\n", c.id, itemName)
}
func (c *customer) getID() string {
return c.id
}
在上面的代碼中item是subject的實現,customer是observer實現。
看下場景類main.go:
func main() {
shirtItem := newItem("GoLang Design Patterns")
observerFirst := &customer{id: "robin@zhyea.com"}
observerSecond := &customer{id: "golang@zhyea.com"}
shirtItem.register(observerFirst)
shirtItem.register(observerSecond)
shirtItem.updateAvailability()
}
執行后輸出內容為:
Item GoLang Design Patterns is now in stock Sending email to customer robin@zhyea.com for item GoLang Design Patterns Sending email to customer golang@zhyea.com for item GoLang Design Patterns
代碼已上傳至GitHub: zhyea / go-patterns / observer-pattern
END!
