![Спостерігач](/images/patterns/cards/observer-mini.png?id=fd2081ab1cff29c60b499bcf6a62786a)
Спостерігач на Go
Спостерігач — це поведінковий патерн, який дозволяє об’єктам повідомляти інші об’єкти про зміни свого стану.
При цьому спостерігачі можуть вільно підписуватися і відписуватись від цих повідомлень.
Концептуальний приклад
На сайті інтернет-магазину періодично може закінчуватися певний товар. Водночас деякі користувачі можуть бути зацікавлені у цьому предметі, якого поки що немає у наявності. У цієї проблеми може бути 3 варіанти вирішення:
- Покупець самостійно періодично перевіряє наявність товару.
- Інтернет-магазин завалює користувачів сповіщеннями про надходження всіх нових товарів.
- Користувач підписується лише на той конкретний предмет, який його цікавить, і одержує сповіщення про його повернення на полиці магазину. Також, на один і той же продукт можуть підписатися декілька покупців.
Варіант 3 звучить найбільш ефективно, і фактично це і є суть патерна Спостерігач. Головні елементи цього патерна проектування наступні:
- Видавець — публікує подію, коли щось відбувається.
- Спостерігач — підписується на події суб’єкта і одержує сповіщення в разі їх виникнення.
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
На основі: Golang By Example