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 — зависимость «один ко многим» с автоматическим оповещением.
- Подписчики регистрируются сами; субъект знает лишь абстрактный интерфейс.
- Не забывайте про отписку, иначе — утечки памяти.