Обзор
Цепочка ответственности — это шаблон поведенческого проектирования, который позволяет передавать запрос по цепочке команд без необходимости связывать класс, отправляющий запрос, с классом, который его получает.
Пример из реального мира
Описание этого узора немного сложное, поэтому представим, что мы находимся в магазине, только что сделали покупки и подходим к кассовому аппарату. Кассир уже проверил наши покупки и ждет оплаты. У нас есть несколько видов оплаты на выбор:
- ваучер—до 20 долларов США.
- наличными— до 100 долларов США
- кредитная карта— до 200 долларов США
Пример кода
Давайте создадим первый класс, который будет отвечать за обработку платежей. Чтобы создать его, нам нужно будет предоставить какой-либо тип оплаты: карта, наличные или ваучер. Дополнительно класс реализует два дополнительных метода:
- успех — если транзакция прошла успешно, возвращается сообщение в зависимости от того, чем мы заплатили
- ошибка — показывает информацию о том, что транзакция не удалась
class PaymentHandler attr_accessor :successor def initialize(successor = nil) @successor = successor end def call(price) successor ? successor.call(price) : error end private def success(message) message end def error "You are poor and have nothing to pay!" end end
Первым способом оплаты мы попробуем использовать ваучер. Итак, давайте реализуем класс, который будет обрабатывать этот платеж. Ваучер можно использовать для оплаты покупок на сумму до 20 долларов США.
class VoucherPaymentHandler < PaymentHandler def call(price) price < 20 ? success('You paid by Voucher!') : super(price) end end
Следующим шагом будет реализация обработчика cash.
class CashPaymentHandler < PaymentHandler def call(price) price < 100 ? success('You paid by Cash!') : super(price) end end
И обработчик карты.
class CardPaymentHandler < PaymentHandler def call(price) price < 200 ? success('You paid by Card!') : super(price) end end
На этом этапе мы можем реализовать нашу цепочку.
payment = VoucherPaymentHandler.new(CashPaymentHandler.new(CardPaymentHandler.new)) payment.call(10) => "You paid by Voucher!" payment.call(50) => "You paid by Cash!" payment.call(150) => "You paid by Card!" payment.call(420) => "You are poor and have nothing to pay!"
В зависимости от того, чем мы платим, методы возвращают успех или, если они не могут обработать платеж, они передают его до последнего звена в цепочке, которое возвращает ошибку в случае неудачи.
Хорошо подготовленная цепочка позволяет обрабатывать бесконечное количество реализаций и размещать конкретный обработчик в любом месте цепочки.