Что это такое
Паттерн Observer («Наблюдатель») позволяет одному объекту объявлять о событиях многим подписчикам, не зная, кто они. Подписчики регистрируются один раз; после этого каждое событие автоматически доходит до всех.
Издатель зависит только от небольшого интерфейса подписчика — новые виды слушателей появляются без единой изменённой строки в издателе.
Пример: одно оповещение — все подписчики
Монитор хранит список подписчиков. Когда что-то происходит, он уведомляет весь список — сегодня это оповещение по электронной почте и панель мониторинга; завтра — сообщение в чат, достаточно просто его подписать.
Опубликовать один раз, доставить всем
Один вызов Alert() — две доставки. Монитор не имеет ни малейшего понятия, что такое электронное письмо или панель мониторинга: он знает только объекты, которые можно уведомить.
Вывод: программа на Go и захваченный вывод
package main
import "fmt"
type Subscriber interface {
Notify(event string)
}
type EmailAlert struct {
addr string
}
func (e EmailAlert) Notify(event string) {
fmt.Println("письмо на", e.addr+":", event)
}
type Dashboard struct{}
func (Dashboard) Notify(event string) {
fmt.Println("панель показывает:", event)
}
// Издатель хранит список и уведомляет всех, кто в нём есть.
type Monitor struct {
subscribers []Subscriber
}
func (m *Monitor) Subscribe(s Subscriber) {
m.subscribers = append(m.subscribers, s)
}
func (m *Monitor) Alert(event string) {
for _, s := range m.subscribers {
s.Notify(event)
}
}
func main() {
m := &Monitor{}
m.Subscribe(EmailAlert{addr: "[email protected]"})
m.Subscribe(Dashboard{})
m.Alert("диск почти заполнен")
}
Когда его использовать
Используйте, когда на одни и те же события должны реагировать несколько частей программы и список реагирующих меняется — обновления интерфейса, оповещения, сброс кеша, ленты активности.
Не используйте, когда получатель ровно один и он неизменен — вызывайте его напрямую. В конкурентных программах на Go сначала рассмотрите каналы: в них уже встроены передача событий с обратным давлением и select.
Плюсы и минусы
Плюсы: издатель и подписчики остаются несвязанными; подписчики появляются и исчезают во время выполнения; новые реакции не требуют изменений в издателе.
Минусы: поток выполнения становится невидимым — на вопрос «кто на это реагирует?» приходится искать ответ; медленный или сбойный подписчик может застопорить цикл уведомления; забытые отписки приводят к утечкам.