Что такое шаблон проектирования Chain of Responsibility?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Паттерн Chain of Responsibility (Цепочка ответственности)
Chain of Responsibility — это поведенческий паттерн проектирования, который предоставляет способ передать запрос последовательно через цепь объектов. Каждый объект в цепи решает либо обработать запрос, либо передать его следующему объекту в цепи.
Проблема
Представьте, что нужно обработать заявку через несколько уровней проверки (валидация, проверка прав, проверка баланса). Обычный подход приводит к множеству вложенных условных операторов:
public class RequestProcessor {
public void process(Request request) {
// Уровень 1: Валидация
if (!isValid(request)) {
throw new InvalidRequestException();
}
// Уровень 2: Проверка прав
if (!hasPermission(request)) {
throw new UnauthorizedException();
}
// Уровень 3: Проверка баланса
if (!hasSufficientBalance(request)) {
throw new InsufficientBalanceException();
}
// Уровень 4: Обработка
processRequest(request);
}
}
Проблемы:
- Трудно добавлять новые обработчики
- Тесно связанный код
- Нарушается принцип Single Responsibility
- Сложно перестраивать цепь
Решение: Chain of Responsibility
Базовый интерфейс обработчика:
public abstract class RequestHandler {
protected RequestHandler nextHandler;
public void setNextHandler(RequestHandler nextHandler) {
this.nextHandler = nextHandler;
}
// Шаблонный метод
public void handle(Request request) {
if (canHandle(request)) {
process(request);
// Обработка закончена, не передаём дальше
} else if (nextHandler != null) {
// Передаём следующему обработчику
nextHandler.handle(request);
} else {
// Никто не обработал
throw new UnhandledException("No handler for request: " + request);
}
}
protected abstract boolean canHandle(Request request);
protected abstract void process(Request request);
}
Конкретные обработчики:
// Обработчик валидации
public class ValidationHandler extends RequestHandler {
@Override
protected boolean canHandle(Request request) {
// Этот обработчик всегда срабатывает первым
return true;
}
@Override
protected void process(Request request) {
System.out.println("Validating request...");
if (request.getAmount() <= 0) {
throw new InvalidRequestException("Amount must be positive");
}
if (request.getUserId() == null) {
throw new InvalidRequestException("User ID is required");
}
System.out.println("Validation passed");
// Передаём дальше
if (nextHandler != null) {
nextHandler.handle(request);
}
}
}
// Обработчик проверки прав
public class AuthorizationHandler extends RequestHandler {
private AuthService authService;
public AuthorizationHandler(AuthService authService) {
this.authService = authService;
}
@Override
protected boolean canHandle(Request request) {
return true;
}
@Override
protected void process(Request request) {
System.out.println("Checking permissions...");
if (!authService.hasPermission(request.getUserId(), "TRANSFER_MONEY")) {
throw new UnauthorizedException(
"User " + request.getUserId() + " has no permission"
);
}
System.out.println("Authorization passed");
if (nextHandler != null) {
nextHandler.handle(request);
}
}
}
// Обработчик проверки баланса
public class BalanceHandler extends RequestHandler {
private AccountService accountService;
public BalanceHandler(AccountService accountService) {
this.accountService = accountService;
}
@Override
protected boolean canHandle(Request request) {
return true;
}
@Override
protected void process(Request request) {
System.out.println("Checking balance...");
BigDecimal balance = accountService.getBalance(request.getUserId());
if (balance.compareTo(request.getAmount()) < 0) {
throw new InsufficientBalanceException(
"Balance " + balance + " < Amount " + request.getAmount()
);
}
System.out.println("Balance check passed");
if (nextHandler != null) {
nextHandler.handle(request);
}
}
}
// Финальный обработчик - обработка запроса
public class ProcessingHandler extends RequestHandler {
private TransactionService transactionService;
public ProcessingHandler(TransactionService transactionService) {
this.transactionService = transactionService;
}
@Override
protected boolean canHandle(Request request) {
return true;
}
@Override
protected void process(Request request) {
System.out.println("Processing transaction...");
Transaction transaction = transactionService.executeTransfer(
request.getUserId(),
request.getRecipient(),
request.getAmount()
);
System.out.println("Transaction completed: " + transaction.getId());
// Это последний обработчик, не передаём дальше
}
}
Конфигурация цепи:
@Configuration
public class ChainConfiguration {
@Bean
public RequestHandler requestChain(
AuthService authService,
AccountService accountService,
TransactionService transactionService) {
// Строим цепь в нужном порядке
RequestHandler validation = new ValidationHandler();
RequestHandler authorization = new AuthorizationHandler(authService);
RequestHandler balance = new BalanceHandler(accountService);
RequestHandler processing = new ProcessingHandler(transactionService);
// Связываем обработчики
validation.setNextHandler(authorization);
authorization.setNextHandler(balance);
balance.setNextHandler(processing);
return validation; // Возвращаем первый обработчик
}
}
Использование:
@Service
public class MoneyTransferService {
private final RequestHandler chain;
public MoneyTransferService(RequestHandler chain) {
this.chain = chain;
}
public void transfer(String userId, String recipient, BigDecimal amount) {
Request request = new Request(userId, recipient, amount);
chain.handle(request); // Запускаем цепь
}
}
// Использование в контроллере
@RestController
public class TransferController {
private final MoneyTransferService transferService;
@PostMapping("/api/transfer")
public ResponseEntity<String> transfer(@RequestBody TransferRequest req) {
try {
transferService.transfer(
req.getUserId(),
req.getRecipient(),
req.getAmount()
);
return ResponseEntity.ok("Transfer successful");
} catch (Exception e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
}
Вывод цепи:
Validating request...
Validation passed
Checking permissions...
Authorization passed
Checking balance...
Balance check passed
Processing transaction...
Transaction completed: TXN-12345
Альтернатива: с использованием фильтров (более гибко)
functional interface RequestFilter {
void filter(Request request, FilterChain chain);
}
public class FilterChain {
private List<RequestFilter> filters = new ArrayList<>();
private int currentIndex = 0;
public void addFilter(RequestFilter filter) {
filters.add(filter);
}
public void process(Request request) {
if (currentIndex < filters.size()) {
RequestFilter filter = filters.get(currentIndex++);
filter.filter(request, this);
}
}
}
// Использование
FilterChain chain = new FilterChain();
chain.addFilter((request, next) -> {
System.out.println("Filter 1");
next.process(request);
});
chain.addFilter((request, next) -> {
System.out.println("Filter 2");
next.process(request);
});
chain.process(request);
Реальные примеры Chain of Responsibility
1. Servlet Filters в Spring:
public class LoggingFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
System.out.println("Request: " + request.getRequestURI());
filterChain.doFilter(request, response); // Передаём дальше
System.out.println("Response status: " + response.getStatus());
}
}
2. Exception handling в Java:
try {
// код
} catch (SpecificException e) {
// обработка 1
} catch (GeneralException e) {
// обработка 2
}
3. Event listeners:
public interface EventListener {
void onEvent(Event event);
}
public class EventDispatcher {
private List<EventListener> listeners = new ArrayList<>();
public void dispatch(Event event) {
for (EventListener listener : listeners) {
listener.onEvent(event);
}
}
}
Преимущества паттерна
- Слабая связанность — обработчики независимы друг от друга
- Single Responsibility — каждый обработчик отвечает за одно
- Open/Closed — легко добавлять новые обработчики
- Гибкость — порядок обработчиков можно изменять динамически
- Разделение ответственности — логика распределена по разным классам
Когда использовать
- Множество объектов, которые могут обработать запрос
- Заранее неизвестен, кто именно обработает запрос
- Нужна возможность динамически менять цепь обработки
- Нужна обработка в несколько этапов с проверками
Chain of Responsibility — мощный паттерн для создания гибких и расширяемых систем обработки запросов, особенно популярен в фреймворках и middleware архитектурах.