![Цепочка обязанностей](/images/patterns/cards/chain-of-responsibility-mini.png?id=36d85eba8d14986f053123de17aac7a7)
Цепочка обязанностей на Python
Цепочка обязанностей — это поведенческий паттерн, позволяющий передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос.
Избавляет от жёсткой привязки отправителя запроса к его получателю, позволяя выстраивать цепь из различных обработчиков динамически.
Сложность:
Популярность:
Применимость: Паттерн встречается в Python не так уж часто, так как для его применения нужна цепь объектов, например, связанный список.
Признаки применения паттерна: Цепочку обязанностей можно определить по спискам обработчиков или проверок, через которые пропускаются запросы. Особенно если порядок следования обработчиков важен.
Концептуальный пример
Этот пример показывает структуру паттерна Цепочка обязанностей, а именно — из каких классов он состоит, какие роли эти классы выполняют и как они взаимодействуют друг с другом.
main.py: Пример структуры паттерна
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any, Optional
class Handler(ABC):
"""
Интерфейс Обработчика объявляет метод построения цепочки обработчиков. Он
также объявляет метод для выполнения запроса.
"""
@abstractmethod
def set_next(self, handler: Handler) -> Handler:
pass
@abstractmethod
def handle(self, request) -> Optional[str]:
pass
class AbstractHandler(Handler):
"""
Поведение цепочки по умолчанию может быть реализовано внутри базового класса
обработчика.
"""
_next_handler: Handler = None
def set_next(self, handler: Handler) -> Handler:
self._next_handler = handler
# Возврат обработчика отсюда позволит связать обработчики простым
# способом, вот так:
# monkey.set_next(squirrel).set_next(dog)
return handler
@abstractmethod
def handle(self, request: Any) -> str:
if self._next_handler:
return self._next_handler.handle(request)
return None
"""
Все Конкретные Обработчики либо обрабатывают запрос, либо передают его
следующему обработчику в цепочке.
"""
class MonkeyHandler(AbstractHandler):
def handle(self, request: Any) -> str:
if request == "Banana":
return f"Monkey: I'll eat the {request}"
else:
return super().handle(request)
class SquirrelHandler(AbstractHandler):
def handle(self, request: Any) -> str:
if request == "Nut":
return f"Squirrel: I'll eat the {request}"
else:
return super().handle(request)
class DogHandler(AbstractHandler):
def handle(self, request: Any) -> str:
if request == "MeatBall":
return f"Dog: I'll eat the {request}"
else:
return super().handle(request)
def client_code(handler: Handler) -> None:
"""
Обычно клиентский код приспособлен для работы с единственным обработчиком. В
большинстве случаев клиенту даже неизвестно, что этот обработчик является
частью цепочки.
"""
for food in ["Nut", "Banana", "Cup of coffee"]:
print(f"\nClient: Who wants a {food}?")
result = handler.handle(food)
if result:
print(f" {result}", end="")
else:
print(f" {food} was left untouched.", end="")
if __name__ == "__main__":
monkey = MonkeyHandler()
squirrel = SquirrelHandler()
dog = DogHandler()
monkey.set_next(squirrel).set_next(dog)
# Клиент должен иметь возможность отправлять запрос любому обработчику, а не
# только первому в цепочке.
print("Chain: Monkey > Squirrel > Dog")
client_code(monkey)
print("\n")
print("Subchain: Squirrel > Dog")
client_code(squirrel)
Output.txt: Результат выполнения
Chain: Monkey > Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut
Client: Who wants a Banana?
Monkey: I'll eat the Banana
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.
Subchain: Squirrel > Dog
Client: Who wants a Nut?
Squirrel: I'll eat the Nut
Client: Who wants a Banana?
Banana was left untouched.
Client: Who wants a Cup of coffee?
Cup of coffee was left untouched.