GoLang設計模式13 - 觀察者模式

robin·張發表於2021-11-07

觀察者模式是一種行為型設計模式。這種模式允許一個例項(可以稱為目標物件)釋出各種事件(event)給其他例項(觀察者)。這些觀察者會對目標物件進行訂閱,這樣每當目標物件發生變化時,觀察者就會收到事件(event)通知。

來看個例子:在電商網站經常會有各種商品脫銷,假如有顧客已經在關注這些商品,這些商品的脫銷就會對他們產生不好的體驗。如果顧客還想買這些商品,那麼通常有如下解決方案:

  1. 顧客以一定的頻率檢查這些商品是否在售
  2. 電商平臺將所有的上架的商品資訊定期推送給使用者
  3. 顧客只訂閱他所關注的特定商品的資訊,當這些商品再次上架時他們會收到通知;多個顧客可以訂閱同一個商品的資訊

選項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!

相關文章