Глянь мій новий курс по Git! Привіт! Глянь мій новий курс по Git! Привіт! Глянь мій новий курс по Git на GitByBit.com! Привіт! Хочеш класно освіжити Git? Глянь мій новий курс на GitByBit.com!
Спостерігач

Спостерігач на 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