Весняний РОЗПРОДАЖ
Спостерігач

Спостерігач на Go

Спостерігач — це поведінковий патерн, який дозволяє об’єктам повідомляти інші об’єкти про зміни свого стану.

При цьому спостерігачі можуть вільно підписуватися і відписуватись від цих повідомлень.

Концептуальний приклад

На сайті інтернет-магазину періодично може закінчуватися певний товар. Водночас деякі користувачі можуть бути зацікавлені у цьому предметі, якого поки що немає у наявності. У цієї проблеми може бути 3 варіанти вирішення:

  1. Покупець самостійно періодично перевіряє наявність товару.
  2. Інтернет-магазин завалює користувачів сповіщеннями про надходження всіх нових товарів.
  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

Спостерігач іншими мовами програмування

Спостерігач на C# Спостерігач на C++ Спостерігач на Java Спостерігач на PHP Спостерігач на Python Спостерігач на Ruby Спостерігач на Rust Спостерігач на Swift Спостерігач на TypeScript