Observer

Паттерн «издатель-подписчик»: при изменении одного объекта автоматически оповещаются все зависимые.

Observer задаёт зависимость «один ко многим»: когда субъект меняет состояние, все его наблюдатели автоматически получают уведомление.

Какую задачу решает

Когда цена акции меняется, обновиться должны: график, табло, журнал. Жёстко прописать в источнике все эти вызовы — значит связать его со всеми потребителями и править при каждом новом потребителе. Observer переворачивает связь: потребители подписываются сами, а субъект знает лишь абстрактный список подписчиков.

Идея и реализация

class Subject:
    def __init__(self):
        self._observers = []
        self._state = None

    def subscribe(self, observer):
        self._observers.append(observer)

    def unsubscribe(self, observer):
        self._observers.remove(observer)

    def set_state(self, value):
        self._state = value
        self._notify()

    def _notify(self):
        for obs in self._observers:
            obs.update(self._state)


class Display:
    def __init__(self, name):
        self.name = name

    def update(self, state):
        print(f"{self.name} показывает: {state}")


stock = Subject()
stock.subscribe(Display("График"))
log = Display("Журнал")
stock.subscribe(log)

stock.set_state(101)
stock.unsubscribe(log)        # отписка
stock.set_state(102)

Вывод:

График показывает: 101
Журнал показывает: 101
График показывает: 102

Субъект знает только метод update у подписчиков. Добавить нового потребителя — вызвать subscribe, и субъект менять не нужно. После unsubscribe(log) журнал перестаёт получать обновления, что видно во втором уведомлении.

Главная архитектурная выгода — инверсия направления зависимости. Без паттерна источник данных вынужден импортировать и знать каждый класс-потребитель, то есть высокоуровневый «график» и «журнал» диктовали бы устройство низкоуровневого источника. С Observer зависимость направлена наоборот: потребители зависят от субъекта, а субъект — лишь от абстрактного интерфейса наблюдателя. Это позволяет держать ядро системы независимым от UI и периферии, добавляя и убирая потребителей как плагины.

Push и pull

Есть два стиля. Push: субъект сам передаёт данные в update(state) (как выше). Pull: субъект сообщает «я изменился», а наблюдатель сам запрашивает нужное через ссылку на субъект. Push проще, pull гибче, когда наблюдателям нужны разные части состояния.

Подводные камни

  • Утечки памяти: забытая отписка держит наблюдателя живым (в JS/Java особенно). Всегда предусматривайте unsubscribe.
  • Каскады: уведомление может запускать новые изменения и лавину оповещений — следите за циклами.

Где встречается

Это фундамент событийной модели: GUI-события (клик → обработчики), реактивность во фронтенде (Vue/React state → перерисовка), брокеры сообщений (pub/sub), сигналы Django, EventEmitter в Node. Observer — один из самых распространённых паттернов вообще.

Итог

  • Observer — зависимость «один ко многим» с автоматическим оповещением.
  • Подписчики регистрируются сами; субъект знает лишь абстрактный интерфейс.
  • Не забывайте про отписку, иначе — утечки памяти.
Проверьте себя
1. Какую зависимость задаёт Observer?
AОдин к одному
BОдин ко многим: субъект — много наблюдателей
CМногие ко многим обязательно
DНикакой
2. Кто инициирует связь в Observer?
AСубъект жёстко прописывает всех потребителей
BНаблюдатели сами подписываются через subscribe
CСвязь задаётся при компиляции
DСвязи нет
3. Частая ошибка при использовании Observer?
AСлишком быстрая отрисовка
BЗабытая отписка, ведущая к утечкам памяти
CОтсутствие наследования
DЛишние интерфейсы
Поддержать проект