
Observer を Go で
Observer は、 振る舞いに関するデザインパターンの一つで、 オブジェクトが別のオブジェクトに状態の変化を通知できるようにします。
Observer パターンは、 サブスクライバー・インターフェースを実装するいかなるオブジェクトのイベント通知の申し込みと停止をする方法を提供します。
概念的な例
電子商取引のウェブサイトでは、 商品が時々在庫切れになります。 在庫切れとなった商品に興味を持っていた顧客にどう対処するかという問題には三つの解決策があります:
- 顧客に入荷したかどうかを定期的にチェックしてもらう。
- 電子商取引サイトが、 新商品入荷のメールを多数送りつける。
- 顧客は興味のある特定商品のみに関して、 入荷したら通知を受けるよう登録する。 複数の顧客が同一商品に通知登録することも可能。
三つ目の解決策が一番妥当で、 これこそが Observer パターンです。 Observer パターンの主要構成要素は:
- サブジェクトは、 何かが起きた時にイベントを発行するインスタンスです。
- オブザーバーは、 サブジェクトのイベントの通知を申し込み、 イベントが起きると通知されます。
subject.go: サブジェクト
package main
type Subject interface {
register(observer Observer)
deregister(observer Observer)
notifyAll()
}
item.go: 具象サブジェクト
package main
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
}
observer.go: オブザーバー
package main
type Observer interface {
update(string)
getID() string
}
customer.go: 具象オブザーバー
package main
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
}
main.go: クライアント・コード
package main
func main() {
shirtItem := newItem("Nike Shirt")
observerFirst := &Customer{id: "abc@gmail.com"}
observerSecond := &Customer{id: "xyz@gmail.com"}
shirtItem.register(observerFirst)
shirtItem.register(observerSecond)
shirtItem.updateAvailability()
}
output.txt: 実行結果
Item Nike Shirt is now in stock
Sending email to customer abc@gmail.com for item Nike Shirt
Sending email to customer xyz@gmail.com for item Nike Shirt