Ответ
Паттерн Chain of Responsibility позволяет передавать запрос по цепочке обработчиков. Каждый обработчик решает, обработать ли запрос самому или передать дальше по цепочке.
Классический пример: Система логирования с разными уровнями серьезности.
// 1. Абстрактный базовый класс обработчика
public abstract class LoggerHandler
{
protected LogLevel _level;
protected LoggerHandler? _nextHandler; // Ссылка на следующий в цепочке
public LoggerHandler(LogLevel level)
{
_level = level;
}
// Метод для установки следующего обработчика
public LoggerHandler SetNext(LoggerHandler nextHandler)
{
_nextHandler = nextHandler;
return nextHandler; // Позволяет строить цепочку fluent-стилем
}
// Основной метод обработки запроса
public void LogMessage(LogLevel level, string message)
{
if (_level <= level) // Если уровень серьезности достаточен для этого обработчика
{
Write(message); // Обрабатываем
}
// Передаем запрос дальше по цепочке, независимо от того, обработали мы его или нет
_nextHandler?.LogMessage(level, message);
}
protected abstract void Write(string message); // Конкретная реализация записи
}
// 2. Конкретные обработчики
public class ConsoleLogger : LoggerHandler
{
public ConsoleLogger(LogLevel level) : base(level) { }
protected override void Write(string message)
{
Console.WriteLine($"[CONSOLE] {DateTime.Now}: {message}");
}
}
public class FileLogger : LoggerHandler
{
private readonly string _filePath;
public FileLogger(LogLevel level, string filePath) : base(level)
{
_filePath = filePath;
}
protected override void Write(string message)
{
File.AppendAllText(_filePath, $"[FILE] {DateTime.Now}: {message}" + Environment.NewLine);
}
}
public class EmailLogger : LoggerHandler
{
public EmailLogger(LogLevel level) : base(level) { }
protected override void Write(string message)
{
// Симуляция отправки email только для критических ошибок
Console.WriteLine($"[EMAIL ALERT] Критическая ошибка: {message}");
}
}
// 3. Использование
public enum LogLevel { Info = 1, Warning = 2, Error = 3, Critical = 4 }
class Program
{
static void Main()
{
// Построение цепочки: Console -> File -> Email
var consoleLogger = new ConsoleLogger(LogLevel.Info); // Логирует всё (Info и выше)
var fileLogger = new FileLogger(LogLevel.Warning, "log.txt"); // Логирует Warning и выше
var emailLogger = new EmailLogger(LogLevel.Critical); // Логирует только Critical
// Связывание обработчиков в цепочку
consoleLogger.SetNext(fileLogger).SetNext(emailLogger);
// Отправка сообщений в начало цепочки
consoleLogger.LogMessage(LogLevel.Info, "Приложение запущено.");
// Вывод: [CONSOLE] ... (File и Email проигнорируют Info)
consoleLogger.LogMessage(LogLevel.Warning, "Загрузка CPU высокая.");
// Вывод: [CONSOLE] ... и запись в файл (Email проигнорирует Warning)
consoleLogger.LogMessage(LogLevel.Critical, "База данных недоступна!");
// Вывод: [CONSOLE] ..., запись в файл И отправка email-алерта
}
}
Где еще применяется:
- Обработка HTTP-запросов в Middleware пайплайне ASP.NET Core (каждый middleware — звено цепи).
- Валидация данных (цепочка проверок).
- Обработка событий в UI.
Преимущества:
- Уменьшает связанность: отправитель запроса не знает, кто его обработает.
- Позволяет динамически менять цепочку.
- Упрощает добавление новых обработчиков.
Ответ 18+ 🔞
Давай разберём этот паттерн, а то звучит как какая-то секта, а на деле всё просто, как три копейки.
Представь, что у тебя есть запрос — ну, какое-нибудь сообщение или событие. И куча желающих с ним что-то сделать. Вместо того чтобы создавать монстра, который знает про всех и вся, ты выстраиваешь этих ребят в цепочку, как в детском саду за ручки. И пускаешь запрос по этой цепочке. Каждый смотрит: «О, это моё?» Если его — обрабатывает. А потом, что самое главное, всегда передаёт следующему, даже если сам уже всё сделал. Ну или не передаёт, если решил, что хватит — это уже вариации.
Вот смотри, живой пример из кода выше — система логирования. У нас есть три упыря:
- Консольный логгер (
ConsoleLogger) — скромняга, пишет всё подряд, начиная с обычных информационных сообщений (Info). - Файловый логгер (
FileLogger) — уже построже. Беспокоить его стоит только с предупреждениями (Warning) и хуже. Инфошный мусор он в файл тащить не станет. - Почтовый логгер (
EmailLogger) — совсем параноик. Его поднимают только по тревоге, когда всё горит и критическая ошибка (Critical).
Суть в том, что мы их связываем в цепочку: Консоль -> Файл -> Почта. И кидаем запрос в первого.
- «Приложение запущено» (уровень
Info). Консольник говорит: «Моё!» — и пишет в консоль. Файльник смотрит: «Инфа? Да пошло оно нахуй, не мой уровень». Но цепочку не рвёт! Он тупо передаёт запрос дальше почтальону. Почтальон тоже игнорирует. Всё. - «Загрузка CPU высокая» (уровень
Warning). Консольник: «Моё!» — пишет. Файльник: «О, warning! Моё!» — дописывает в файл. Почтальон: «Ещё не критично» — игнорирует, но передачу не прерывает. - «База данных недоступна!» (уровень
Critical). А вот тут веселье. Консольник пишет, Файльник пишет, а Почтальон, получив запрос, орет: «ЁБАНАРОТ! КРИТИЧЕСКАЯ ОШИБКА!» — и симулирует отправку алерта.
Красота в чём? В том, что тому, кто кидает сообщение (в Main), вообще похуй, кто и как его обработает. Он кинул в первого — и забыл. А если завтра понадобится добавить, скажем, TelegramLogger, который будет слать сообщения в чат, ты просто создашь новый класс и встроишь его в цепочку между Файлом и Почтой. Ничего ломать не нужно. Гибко, модульно, а главное — каждый класс отвечает только за свою епархию.
Где это в жизни? Да везде!
- Тот самый пайплайн middleware в ASP.NET Core — это и есть чистейшая цепочка обязанностей. Запрос пришёл, прошёлся по цепочке мидлварей (логирование, аутентификация, авторизация, роутинг), каждый сделал своё дело.
- Валидация формы. Одна проверка на пустоту, вторая на формат email, третья на сложность пароля — все выстроены в цепь. Первая проверка упала — остальные могут даже не запускаться (это уже вариация).
- Обработка событий в интерфейсах. Всплыло окошко — обработалось, не обработалось — событие всплывает дальше к родительскому элементу.
Итог: Паттерн Chain of Responsibility — это когда ты не заставляешь одного мудака знать всё на свете, а создаёшь отряд спецов, выстраиваешь их в шеренгу и пускаешь задание по цепочке. Каждый делает что может (или не делает), но обязан передать эстафету дальше. Удобно, расширяемо, и каждый в ответе только за свой кусок.