Шаблон цепочки обязанностей

Шаблон цепочки обязанностей создаёт цепочку объектов-получателей для запроса. Этот шаблон устраняет связывание отправителя и получателя запроса в зависимости от типа запроса. Этот тип шаблона проектирования относится к поведенческим шаблонам.

Шаблон цепочки обязанностей соединяет несколько обработчиков (обрабатывающих объектов) в цепочку, позволяя передавать запрос по цепочке до тех пор, пока какой-либо обработчик не обработает его.

Шаблон цепочки обязанностей позволяет нескольким объектам иметь возможность обработать запрос, тем самым избегая связывания отправителя и получателя запроса. Эти объекты соединяются в цепочку, и запрос передаётся по цепочке.

Введение

назначение

Позволяет передавать запрос по цепочке обработчиков до тех пор, пока запрос не будет обработан.

Основные решаемые проблемы

  • Разделяет отправителя и получателя запроса, позволяя нескольким объектам иметь возможность получить запрос, при этом отправителю не нужно знать, какой объект будет его обрабатывать.

Сценарий использования

  • Когда есть несколько объектов, которые могут обработать запрос, и то, какой конкретный объект его обработает, определяется во время выполнения.
  • Когда нужно отправить запрос одному из нескольких объектов, не указывая явно получателя.

Подход к реализации

  • Определите интерфейс обработчика: Все обработчики должны реализовывать один и тот же интерфейс.
  • Создайте конкретные обработчики: Конкретные классы, реализующие интерфейс, содержащие логику обработки запроса и ссылку на следующий обработчик в цепочке.

Ключевой код

  • Интерфейс Handler: Определите метод для обработки запросов.
  • Класс ConcreteHandler: Реализует интерфейс Handler, содержит логику обработки запроса и ссылку на следующий обработчик.

Пример приложения

  1. Горячая картошка: Поведение передачи в игре, пока музыка не остановится.
  2. Всплытие событий: В JavaScript события начинаются с самого конкретного элемента и распространяются вверх уровень за уровнем.
  3. Веб-серверПримеры включают Apache Tomcat, обрабатывающий кодировку символов, перехватчики Struts2 и Servlet-фильтры.

Преимущества

  1. Снижение связанности: Разъедините отправителя и получателя.
  2. Упрощение объектов: объекту не нужно знать структуру цепочки.
  3. Гибкость: динамически добавлять или удалять обязанности, изменяя участников или порядок цепочки.
  4. Легко расширять: удобно добавлять новые классы обработчиков запросов.

Недостатки

  1. Запрос не обработан: нет гарантии, что запрос будет получен обработчиком в цепочке.
  2. Влияние на производительность: может влиять на производительность системы, отладка затруднена, может привести к циклическим вызовам.
  3. Сложно наблюдать: характеристики времени выполнения неочевидны, что может затруднить отладку.

Рекомендации по использованию

  • При обработке запроса, если есть несколько потенциальных обработчиков, рассмотрите использование паттерна «Цепочка обязанностей».
  • Убедитесь, что каждый обработчик в цепочке чётко знает, как передать запрос следующему звену цепочки.

Примечания

  • В Java Web-разработке паттерн «Цепочка обязанностей» широко используется, например, в цепочках фильтров, перехватчиках и т. д.

Структура

В основном он включает следующие ключевые роли:

  • Абстрактный обработчик:

    • Определяет интерфейс для обработки запросов, обычно содержащий метод для обработки запроса (например,handleRequest) и ссылка на следующий обработчик (преемник).
  • Конкретный обработчик:

    • Реализует интерфейс абстрактного обработчика и отвечает за обработку запросов. Если он может обработать запрос, он обрабатывает его напрямую; в противном случае он передаёт запрос следующему обработчику.
  • Клиент:

    • Создаёт объекты обработчиков и соединяет их в цепочку обязанностей. Обычно клиенту достаточно лишь отправить запрос первому обработчику в цепочке, не заботясь о конкретной обработке запроса.

Реализация

Мы создаём абстрактный классAbstractLoggerс детализированными уровнями логирования. Затем мы создаём три типа логгеров, все расширяютAbstractLogger. Каждый логгер проверяет, принадлежит ли уровень сообщения его собственному уровню; если да, он выводит его соответствующим образом; в противном случае он не выводит и передаёт сообщение следующему логгеру.

责任链模式的 UML 图

Шаг 1

Создайте абстрактный класс логгера.

AbstractLogger.java

public abstract class AbstractLogger { public static int INFO = 1; public static int DEBUG = 2; public static int ERROR = 3; protected int level; //Следующий элемент в цепочке обязанностей protected AbstractLogger nextLogger; public void setNextLogger(AbstractLogger nextLogger){ this.nextLogger = nextLogger; } public void logMessage(int level, String message){ if(this.level <= level){ write(message); } if(nextLogger !=null){ nextLogger.logMessage(level, message); } } abstract protected void write(String message); }

Шаг 2

Создайте классы сущностей, которые расширяют класс logger.

ConsoleLogger.java

public class ConsoleLogger extends AbstractLogger { public ConsoleLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Стандартный Console::Logger: " + message); } }

ErrorLogger.java

public class ErrorLogger extends AbstractLogger { public ErrorLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("Ошибка Console::Logger: " + message); } }

FileLogger.java

public class FileLogger extends AbstractLogger { public FileLogger(int level){ this.level = level; } @Override protected void write(String message) { System.out.println("File::Logger: " + message); } }

Шаг 3

Создайте различные типы логгеров. Назначьте им разные уровни ошибок и установите следующий логгер в каждом логгере. Следующий логгер в каждом логгере представляет часть цепочки.

ChainPatternDemo.java

public class ChainPatternDemo { private static AbstractLogger getChainOfLoggers(){ AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR); AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG); AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO); errorLogger.setNextLogger(fileLogger); fileLogger.setNextLogger(consoleLogger); return errorLogger; } public static void main(String[] args) { AbstractLogger loggerChain = getChainOfLoggers(); loggerChain.logMessage(AbstractLogger.INFO, "Это информация."); loggerChain.logMessage(AbstractLogger.DEBUG, "Это информация уровня отладки."); loggerChain.logMessage(AbstractLogger.ERROR, "Это информация об ошибке."); } }

Шаг 4

Выполните программу, выведите результаты:

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.
Другие расширения