Summer SALE
Цепочка обязанностей

Цепочка обязанностей на Java

Цепочка обязанностей — это поведенческий паттерн, позволяющий передавать запрос по цепочке потенциальных обработчиков, пока один из них не обработает запрос.

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

Сложность:

Популярность:

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

Область применения цепочки обязанностей — всевозможные обработчики событий, последовательные проверки доступа и прочее.

Примеры Цепочки обязанностей в стандартных библиотеках Java:

Признаки применения паттерна: Цепочку обязанностей можно определить по спискам обработчиков или проверок, через которые пропускаются запросы. Особенно если порядок следования обработчиков важен.

Слои авторизации и аутентификации пользователей

Этот пример показывает как пользовательские данные проходят последовательную аутентификацию в множестве обработчиков, связанных в одну цепь.

Этот пример отличается от канонической версии тем, что проверка обрывается, если очередной обработчик не может обработать запрос. В классическом варианте, следование по цепочке заканчивается как только нашёлся элемент цепи, который может обработать запрос. Просто знайте, что Концептуальный пример от этого не меняется, а код отличается только условием выхода из цепи.

middleware

middleware/Middleware.java: Базовый класс проверок

package refactoring_guru.chain_of_responsibility.example.middleware; /** * Базовый класс цепочки. */ public abstract class Middleware { private Middleware next; /** * Помогает строить цепь из объектов-проверок. */ public static Middleware link(Middleware first, Middleware... chain) { Middleware head = first; for (Middleware nextInChain: chain) { head.next = nextInChain; head = nextInChain; } return first; } /** * Подклассы реализуют в этом методе конкретные проверки. */ public abstract boolean check(String email, String password); /** * Запускает проверку в следующем объекте или завершает проверку, если мы в * последнем элементе цепи. */ protected boolean checkNext(String email, String password) { if (next == null) { return true; } return next.check(email, password); } } 

middleware/ThrottlingMiddleware.java: Проверка на лимит запросов

package refactoring_guru.chain_of_responsibility.example.middleware; /** * Конкретный элемент цепи обрабатывает запрос по-своему. */ public class ThrottlingMiddleware extends Middleware { private int requestPerMinute; private int request; private long currentTime; public ThrottlingMiddleware(int requestPerMinute) { this.requestPerMinute = requestPerMinute; this.currentTime = System.currentTimeMillis(); } /** * Обратите внимание, вызов checkNext() можно вставить как в начале этого * метода, так и в середине или в конце. * * Это даёт еще один уровень гибкости по сравнению с проверками в цикле. * Например, элемент цепи может пропустить все остальные проверки вперёд и * запустить свою проверку в конце. */ public boolean check(String email, String password) { if (System.currentTimeMillis() > currentTime + 60_000) { request = 0; currentTime = System.currentTimeMillis(); } request++; if (request > requestPerMinute) { System.out.println("Request limit exceeded!"); Thread.currentThread().stop(); } return checkNext(email, password); } } 

middleware/UserExistsMiddleware.java: Проверка пароля

package refactoring_guru.chain_of_responsibility.example.middleware; import refactoring_guru.chain_of_responsibility.example.server.Server; /** * Конкретный элемент цепи обрабатывает запрос по-своему. */ public class UserExistsMiddleware extends Middleware { private Server server; public UserExistsMiddleware(Server server) { this.server = server; } public boolean check(String email, String password) { if (!server.hasEmail(email)) { System.out.println("This email is not registered!"); return false; } if (!server.isValidPassword(email, password)) { System.out.println("Wrong password!"); return false; } return checkNext(email, password); } } 

middleware/RoleCheckMiddleware.java: Проверка роли

package refactoring_guru.chain_of_responsibility.example.middleware; /** * Конкретный элемент цепи обрабатывает запрос по-своему. */ public class RoleCheckMiddleware extends Middleware { public boolean check(String email, String password) { if (email.equals("admin@example.com")) { System.out.println("Hello, admin!"); return true; } System.out.println("Hello, user!"); return checkNext(email, password); } } 

server

server/Server.java: Сервер, на который заходим

package refactoring_guru.chain_of_responsibility.example.server; import refactoring_guru.chain_of_responsibility.example.middleware.Middleware; import java.util.HashMap; import java.util.Map; /** * Класс сервера. */ public class Server { private Map<String, String> users = new HashMap<>(); private Middleware middleware; /** * Клиент подаёт готовую цепочку в сервер. Это увеличивает гибкость и * упрощает тестирование класса сервера. */ public void setMiddleware(Middleware middleware) { this.middleware = middleware; } /** * Сервер получает email и пароль от клиента и запускает проверку * авторизации у цепочки. */ public boolean logIn(String email, String password) { if (middleware.check(email, password)) { System.out.println("Authorization have been successful!"); // Здесь должен быть какой-то полезный код, работающий для // авторизированных пользователей. return true; } return false; } public void register(String email, String password) { users.put(email, password); } public boolean hasEmail(String email) { return users.containsKey(email); } public boolean isValidPassword(String email, String password) { return users.get(email).equals(password); } } 

Demo.java: Клиентский код

package refactoring_guru.chain_of_responsibility.example; import refactoring_guru.chain_of_responsibility.example.middleware.Middleware; import refactoring_guru.chain_of_responsibility.example.middleware.RoleCheckMiddleware; import refactoring_guru.chain_of_responsibility.example.middleware.ThrottlingMiddleware; import refactoring_guru.chain_of_responsibility.example.middleware.UserExistsMiddleware; import refactoring_guru.chain_of_responsibility.example.server.Server; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * Демо-класс. Здесь всё сводится воедино. */ public class Demo { private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); private static Server server; private static void init() { server = new Server(); server.register("admin@example.com", "admin_pass"); server.register("user@example.com", "user_pass"); // Проверки связаны в одну цепь. Клиент может строить различные цепи, // используя одни и те же компоненты. Middleware middleware = Middleware.link( new ThrottlingMiddleware(2), new UserExistsMiddleware(server), new RoleCheckMiddleware() ); // Сервер получает цепочку от клиентского кода. server.setMiddleware(middleware); } public static void main(String[] args) throws IOException { init(); boolean success; do { System.out.print("Enter email: "); String email = reader.readLine(); System.out.print("Input password: "); String password = reader.readLine(); success = server.logIn(email, password); } while (!success); } } 

OutputDemo.txt: Результат выполнения

Enter email: admin@example.com Input password: admin_pass Hello, admin! Authorization have been successful! Enter email: wrong@example.com Input password: wrong_pass This email is not registered! Enter email: wrong@example.com Input password: wrong_pass This email is not registered! Enter email: wrong@example.com Input password: wrong_pass Request limit exceeded! 

Цепочка обязанностей на других языках программирования

Цепочка обязанностей на C# Цепочка обязанностей на C++ Цепочка обязанностей на Go Цепочка обязанностей на PHP Цепочка обязанностей на Python Цепочка обязанностей на Ruby Цепочка обязанностей на Rust Цепочка обязанностей на Swift Цепочка обязанностей на TypeScript