Паттерн Наблюдатель (или Издатель - Подписчик) - поведенческий.
Ключевые идеи Observer'а:
- Взаимодействие между классами не через управляющие методы, а через события.
- Делим классы на две роли
Subject(источник) иObserver(подписчик).
Observer может подписаться или отписаться от Subject.
Благодаря данному паттерну:
- Принцип разделения ответственности SRP соблюдается.
Publisherне зависит от систем подписчиков (у него просто есть список наследников от общегоObserver, и по этому списку он в случае события проходится и вызывает метод).- Распухание кода не грозит, классы могут подписываться и отписываться
Tip
Допустим, у нас будет публикатор постов (Publisher) и различные сервисы (наследуются от Observer), которые его слушают и впоследствии обрабатывают информацию о новом посте.
Publisher использует метод notify, который вызывает у всех подписанных классов метод eventOccurred.
Publisher - наследуется от Subject.
RecomendationEngine, NotificationSender,
AnalyticsCollector, AdvertisingIntegrator - от Observer
Observer'ы подписываются/отписываются от Publisher',
а через методы attach/detach.
Publisher не контролирует, кто именно к нему подписан и
работает со всеми одинаково через интерфейс Observer.
Subject передает Event через метод notify,
а Publisher передает частный случай PostEvent.
from pydantic import BaseModel
from typing import List
from abc import ABC, abstractmethod
class Event:
def __init__(self, type, details):
self.type = type
self.details = details
class Observer(ABC, BaseModel):
@abstractmethod
def eventOccurred(self, event: Event):
pass
class Subject(BaseModel):
_observers: List[Observer]
@abstractmethod
def attach(self, observer: Observer):
pass
@abstractmethod
def detach(self, observer: Observer):
pass
@abstractmethod
def notify(self, event: Event):
pass
class RecomendationEngine(Observer):
def eventOccurred(self, event: Event):
print(
"Я - Движок рекомендаций, и я получил {} с деталями: {}".format(
event.type, event.details
)
)
class NotificationSender(Observer):
def eventOccurred(self, event: Event):
print(
"Я - Отправитель уведомлений, и я получил {} с деталями: {}".format(
event.type, event.details
)
)
class AnalyticsCollector(Observer):
def eventOccurred(self, event: Event):
print(
"Я - Собиратель аналитики, и я получил {} с деталями: {}".format(
event.type, event.details
)
)
class AdvertisingIntegrator(Observer):
def eventOccurred(self, event: Event):
print(
"Я - Интегратор рекламы, и я получил {} с деталями: {}".format(
event.type, event.details
)
)
class PostEvent(Event):
def __init__(self, details):
type = "Post"
super().__init__(type, details)
class Publisher(Subject):
_observers: List[Observer] = []
def attach(self, observer: Observer):
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer: Observer):
if observer in self._observers:
self._observers.remove()
def notify(self, event: Event):
for observer in self._observers:
observer.eventOccurred(event)
def create_post(self, text):
event = PostEvent(text)
print("Создал пост, детали: {}".format(text))
self.notify(event)
recom_eng = RecomendationEngine()
notif_send = NotificationSender()
analyt_coll = AnalyticsCollector()
advert_integr = AdvertisingIntegrator()
publisher = Publisher()
publisher.attach(recom_eng)
publisher.attach(notif_send)
publisher.attach(analyt_coll)
publisher.attach(advert_integr)
publisher.create_post("Видео с милым котиком")
# output:
# Создал пост, детали: Видео с милым котиком
# Я - Движок рекомендаций, и я получил Post с деталями: Видео с милым котиком
# Я - Отправитель уведомлений, и я получил Post с деталями: Видео с милым котиком
# Я - Собиратель аналитики, и я получил Post с деталями: Видео с милым котиком
# Я - Интегратор рекламы, и я получил Post с деталями: Видео с милым котиком