Поведенческие паттерны: Strategy, Chain of Responsibility, Command, Observer, Template Method, Mediator

↑ 8.1 Принципы и паттерны проектирования в C♯ · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: senior

Зачем это на собесе

Самые применимые паттерны: Strategy, Observer, Mediator, Chain — встречаются в ASP.NET Core и MediatR.

Объяснение

ПаттернСутьГде встречается
Strategyвзаимозаменяемые алгоритмы за интерфейсомрасчёт скидок, способы оплаты, IComparer
Chain of Responsibilityзапрос проходит по цепочке обработчиковmiddleware ASP.NET Core, DelegatingHandler
Commandзапрос как объект: можно очередить, логировать, отменятьCQRS-команды, ICommand
Observerподписчики получают уведомлениясобытия C#, IObservable<T>, доменные события
Template Methodскелет алгоритма в базовом классе, шаги переопределяютсяBackgroundService, базовые обработчики
Mediatorобъекты общаются через посредникаMediatR
Stateповедение зависит от состоянияконечные автоматы заказа
Iteratorобход коллекцииIEnumerable, yield
Visitorоперация над структурой без её измененияобход AST, ExpressionVisitor
Mementoсохранение состояния для откатаundo
// Strategy + DI: набор стратегий, выбор по условию
public interface IShipping { bool Supports(string code); decimal Cost(Order o); }
public class ShippingCalculator(IEnumerable<IShipping> all)
{
    public decimal Cost(Order o, string code) => all.First(s => s.Supports(code)).Cost(o);
}
 
// Chain of Responsibility: пайплайн шагов обработки
public delegate Task Step(OrderContext ctx, Func<Task> next);
Step[] pipeline = [Validate, Reserve, Charge];
 
// Mediator: обработчик команды
public record CreateOrder(Guid CustomerId) : IRequest<Guid>;
public class CreateOrderHandler : IRequestHandler<CreateOrder, Guid> { /* ... */ }
var id = await mediator.Send(new CreateOrder(customerId));
 
// Observer: доменное событие
public event Func<OrderPaid, Task>? OrderPaid;

Нюансы и подводные камни

  • Observer с событиями C#: утечки памяти при неотписанных подписчиках (сильные ссылки).
  • Mediator скрывает зависимости между компонентами; в больших системах затрудняет навигацию по коду.
  • Strategy с if/else в фабрике сводит пользу к нулю: выбирайте через DI/ключи.
  • Command объекты должны быть сериализуемыми, если уходят в очередь.

Практика

  1. Замените цепочку if/else расчёта доставки на стратегии.
  2. Реализуйте свой мини-pipeline обработки заказа (Chain).
  3. Сделайте конечный автомат статуса заказа (State).

Вопросы с ответами

Связанные темы