Пример Реализации Паттерна Цепочка Обязанностей
Паттерн «Цепочка Обязанностей» —позволяет создавать цепочку объектов для обработки запроса или выполнения задачи. Рассмотрим, как реализовать его в .NET на практическом примере.
Допустим, у нас есть ряд правил скидок для приложения электронной коммерции. В зависимости от клиента мы хотим применять различные проценты скидок к его заказам:
— VIP - 20%,
— постоянный клиент - 10%,
— новый клиент - 5%.
— иначе не применять скидку.
Изначально мы можем обрабатывать эту логику с помощью ряда операторов if:
public decimal CalculateDiscount(
Customer customer,
decimal orderTotal)
{
if (customer.IsVIP)
return orderTotal * 0.8m;
if (customer.IsRegular)
return orderTotal * 0.9m;
if (customer.IsNew)
return orderTotal * 0.95m;
return orderTotal;
}
Цепочка Обязанностей
Такой подход может стать громоздким, когда количество правил растёт и сами правила усложняются. Отрефакторим код для использования этого паттерна.
1. Создадим абстрактный класс обработчика DiscountHandler, который определяет общий интерфейс для всех обработчиков скидок:
public abstract class DiscountHandler
{
protected DiscountHandler _next;
public DiscountHandler SetNextHandler(
DiscountHandler next)
{
_next = next;
return next;
}
public abstract decimal CalculateDiscount(
Customer customer, decimal orderTotal);
}
2. Реализуем конкретные обработчики скидок, производные от DiscountHandler. Каждый обработчик будет отвечать за определённое правило и решать, применять ли скидку или передавать запрос следующему обработчику:
public class VIPDiscountHandler
: DiscountHandler
{
public override decimal
CalculateDiscount(
Customer customer,
decimal orderTotal)
{
if (customer.IsVIP)
return orderTotal * 0.8m;
return _next?
.CalculateDiscount(customer, orderTotal)
?? orderTotal;
}
}
Остальные реализуем аналогично.
3. Создадим цепочку связанных обработчиков:
var vipHandler = new VIPDiscountHandler();
vipHandler
.SetNextHandler(new RegularDiscountHandler())
.SetNextHandler(new NewCustomerDiscountHandler())
.SetNextHandler(new NoDiscountHandler());
Наконец, вызовем цепочку, обратившись к методу CalculateDiscount первого обработчика:
decimal discountAmount = vipHandler
.CalculateDiscount(customer, orderTotal);
Плюсы
1. Гибкость
Позволяет динамически изменять или расширять цепочку (добавлять или удалять обработчики), не затрагивая другие части кода.
2. Слабая связанность
Каждому обработчику нужно знать только о своем непосредственном преемнике, что минимизирует зависимости.
3. Единая ответственность
Отделяет классы, которые вызывают операции, от классов, которые выполняют операции.
Минусы
1. Запрос может остаться необработанным
Если ни один из обработчиков в цепочке не может обработать запрос, он может остаться необработанным, что приведет к неожиданному поведению. Важно иметь обработчик по умолчанию или способ обработки таких сценариев.
2. Потенциальное влияние на производительность
Если цепочка становится очень длинной, это может привести к накладным расходам производительности из-за обхода нескольких обработчиков.
При применении этого паттерна в реальных сценариях важно соблюдать баланс между количеством обработчиков и соображениями производительности.
Источник: https://thecodeman.net/posts/chain-responsibility-pattern