觀察者模式是一種行為型設計模式。這種模式允許一個例項(可以稱為目標物件)釋出各種事件(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!