Как реализовать паттерн Observer?python-28

Паттерн Observer (Наблюдатель) — это поведенческий паттерн, который позволяет объектам подписываться на события и получать уведомления при их наступлении. Это создает механизм подписки/публикации для рассылки событий множеству объектов.

Основная концепция

Паттерн состоит из двух основных компонентов:

  1. Subject (Субъект) - объект, который содержит состояние и уведомляет наблюдателей о его изменениях
  2. Observer (Наблюдатель) - интерфейс для объектов, которые должны быть уведомлены об изменениях

Базовая реализация

from abc import ABC, abstractmethod

class Observer(ABC):
    """Абстрактный наблюдатель"""
    @abstractmethod
    def update(self, subject):
        """Получение обновления от субъекта"""
        pass

class Subject(ABC):
    """Абстрактный субъект"""
    def __init__(self):
        self._observers = []

    def attach(self, observer):
        """Подписать наблюдателя"""
        if observer not in self._observers:
            self._observers.append(observer)

    def detach(self, observer):
        """Отписать наблюдателя"""
        self._observers.remove(observer)

    def notify(self):
        """Уведомить всех наблюдателей"""
        for observer in self._observers:
            observer.update(self)

Конкретная реализация

class ConcreteSubject(Subject):
    """Конкретный субъект с состоянием"""
    def __init__(self):
        super().__init__()
        self._state = None

    @property
    def state(self):
        return self._state

    @state.setter
    def state(self, value):
        self._state = value
        self.notify()  # Автоматическое уведомление при изменении

class ConcreteObserverA(Observer):
    """Конкретный наблюдатель типа A"""
    def update(self, subject):
        print(f"ObserverA: Reacted to state change: {subject.state}")

class ConcreteObserverB(Observer):
    """Конкретный наблюдатель типа B"""
    def update(self, subject):
        print(f"ObserverB: Reacted to state change: {subject.state}")

Пример использования

if __name__ == "__main__":
    subject = ConcreteSubject()

    observer_a = ConcreteObserverA()
    subject.attach(observer_a)

    observer_b = ConcreteObserverB()
    subject.attach(observer_b)

    subject.state = "First state"  # Оба наблюдателя получат уведомление
    subject.state = "Second state" # Оба наблюдателя получат уведомление

    subject.detach(observer_a)
    subject.state = "Third state"  # Только observer_b получит уведомление

Альтернативные реализации

1. Использование событий

class Event:
    """Класс события"""
    def __init__(self):
        self.handlers = []

    def subscribe(self, handler):
        self.handlers.append(handler)

    def unsubscribe(self, handler):
        self.handlers.remove(handler)

    def fire(self, *args, **kwargs):
        for handler in self.handlers:
            handler(*args, **kwargs)

class Publisher:
    """Издатель событий"""
    def __init__(self):
        self.on_change = Event()

    def do_something(self):
        self.on_change.fire("Something happened!")

2. Использование weakref

import weakref

class WeakRefObserver:
    def __init__(self):
        self._observers = weakref.WeakSet()

    def attach(self, observer):
        self._observers.add(observer)

    def notify(self):
        for observer in self._observers:
            observer.update(self)

Плюсы и минусы

Плюсы:

  • Уменьшает связанность между объектами
  • Позволяет динамически добавлять и удалять наблюдателей
  • Поддерживает принцип открытости/закрытости

Минусы:

  • Наблюдатели оповещаются в случайном порядке
  • Может привести к проблемам с производительностью при множестве наблюдателей
  • Возможны утечки памяти, если не использовать weakref

Реальные примеры использования

  1. Реактивные UI-фреймворки (например, Kivy, PyQt)
  2. Системы логирования
  3. Модели в MVC-архитектуре
  4. Системы обработки событий в играх

Резюмируем

  1. Паттерн Observer реализует механизм подписки/уведомлений
  2. В Python его можно реализовать через абстрактные классы или механизм событий
  3. Для избежания утечек памяти используйте weakref
  4. Паттерн особенно полезен в event-driven архитектурах
  5. В стандартной библиотеке Python есть модуль asyncio, который предоставляет похожий функционал через очереди

Паттерн Observer — мощный инструмент для создания гибких систем с низкой связанностью компонентов.