← Назад к вопросам

Для чего нужен паттерн цепочка ответственности?

2.0 Middle🔥 81 комментариев
#SOLID и паттерны проектирования

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Паттерн Цепочка ответственности (Chain of Responsibility)

Определение

Цепочка ответственности (Chain of Responsibility) — поведенческий паттерн проектирования, который позволяет передавать запросы по цепи обработчиков, где каждый обработчик решает, обработать запрос или передать его следующему в цепи.

Основное назначение

1. Абстракция обработчиков

Паттерн создаёт цепь независимых обработчиков:

// Абстрактный обработчик
public abstract class Handler {
    protected Handler nextHandler;
    
    public void setNext(Handler handler) {
        this.nextHandler = handler;
    }
    
    // Шаблонный метод
    public void handle(Request request) {
        if (canHandle(request)) {
            process(request);
        } else if (nextHandler != null) {
            nextHandler.handle(request);
        }
    }
    
    protected abstract boolean canHandle(Request request);
    protected abstract void process(Request request);
}

2. Конкретные обработчики

Каждый обработчик отвечает за свою задачу:

public class AuthenticationHandler extends Handler {
    @Override
    protected boolean canHandle(Request request) {
        return request.type() == RequestType.AUTHENTICATION;
    }
    
    @Override
    protected void process(Request request) {
        System.out.println("Проверка аутентификации: " + request.getUser());
    }
}

public class AuthorizationHandler extends Handler {
    @Override
    protected boolean canHandle(Request request) {
        return request.type() == RequestType.AUTHORIZATION;
    }
    
    @Override
    protected void process(Request request) {
        System.out.println("Проверка прав доступа: " + request.getPermission());
    }
}

public class LoggingHandler extends Handler {
    @Override
    protected boolean canHandle(Request request) {
        return true;  // Обрабатываем всегда
    }
    
    @Override
    protected void process(Request request) {
        System.out.println("Логирование запроса: " + request.getId());
    }
}

3. Построение цепи

Обработчики объединяются в цепь:

public class RequestProcessor {
    public static void main(String[] args) {
        // Создание обработчиков
        Handler authHandler = new AuthenticationHandler();
        Handler authzHandler = new AuthorizationHandler();
        Handler logHandler = new LoggingHandler();
        
        // Построение цепи
        authHandler.setNext(authzHandler);
        authzHandler.setNext(logHandler);
        
        // Запрос обрабатывается по цепи
        Request request = new Request(RequestType.AUTHENTICATION, "user123");
        authHandler.handle(request);
        // Результат: AuthenticationHandler обработает, затем передаст AuthorizationHandler
    }
}

4. Реальный пример: Обработка запросов в веб-приложении

public class HttpRequest {
    private String method;
    private String path;
    private Map<String, String> headers;
    private String body;
    
    // getters...
}

// Обработчик CORS
public class CorsHandler extends RequestHandler {
    @Override
    public void handle(HttpRequest request) {
        if (request.getHeaders().containsKey("Origin")) {
            System.out.println("CORS проверка: " + request.getHeaders().get("Origin"));
            // Добавляем CORS заголовки в ответ
        }
        
        if (nextHandler != null) {
            nextHandler.handle(request);
        }
    }
}

// Обработчик аутентификации
public class AuthenticationHandler extends RequestHandler {
    @Override
    public void handle(HttpRequest request) {
        String token = request.getHeaders().get("Authorization");
        if (token == null || !isValidToken(token)) {
            System.out.println("Ошибка аутентификации!");
            return;  // Цепь прерывается
        }
        
        System.out.println("Токен валиден: " + token);
        
        if (nextHandler != null) {
            nextHandler.handle(request);
        }
    }
    
    private boolean isValidToken(String token) {
        return token.startsWith("Bearer ");
    }
}

// Обработчик бизнес-логики
public class BusinessLogicHandler extends RequestHandler {
    @Override
    public void handle(HttpRequest request) {
        System.out.println("Обработка запроса: " + request.getMethod() + " " + request.getPath());
        
        if (nextHandler != null) {
            nextHandler.handle(request);
        }
    }
}

// Использование
public class Server {
    public void initChain() {
        RequestHandler cors = new CorsHandler();
        RequestHandler auth = new AuthenticationHandler();
        RequestHandler business = new BusinessLogicHandler();
        
        cors.setNext(auth);
        auth.setNext(business);
        
        HttpRequest request = new HttpRequest("GET", "/api/users");
        request.addHeader("Origin", "http://localhost:3000");
        request.addHeader("Authorization", "Bearer eyJhbGc...");
        
        cors.handle(request);
    }
}

5. Применение в фреймворках

Spring Security (фильтры):

// Spring создаёт цепь фильтров автоматически
@Configuration
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors()     // CORS фильтр
            .and()
            .csrf()     // CSRF защита
            .and()
            .authorizeHttpRequests()
                .requestMatchers("/api/public/**").permitAll()
                .anyRequest().authenticated()
            .and()
            .httpBasic();  // Basic Auth
        
        return http.build();
    }
}
// Внутри Spring автоматически создаёт цепь фильтров

AOP (Aspect-Oriented Programming) — альтернатива:

@Aspect
@Component
public class LoggingAspect {
    @Before("@annotation(com.example.Loggable)")
    public void logBefore(JoinPoint jp) {
        System.out.println("До выполнения: " + jp.getSignature());
    }
    
    @After("@annotation(com.example.Loggable)")
    public void logAfter(JoinPoint jp) {
        System.out.println("После выполнения: " + jp.getSignature());
    }
}

6. Преимущества и недостатки

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

  • Слабая связанность между обработчиками
  • Легко добавлять новые обработчики
  • Порядок обработки гибкий
  • Разделение ответственности

Недостатки:

  • Сложность отладки (откуда взялся результат?)
  • Гарантии обработки нет (может остаться необработанным)
  • Производительность (много объектов в цепи)

7. Где используется

  • Веб фреймворки: Spring Security, middleware в Express.js
  • Логирование: Logger в Java (обработчик эскалирует ошибку выше)
  • GUI компоненты: обработка событий (mouseClick передаётся родителю)
  • REST APIs: middleware для аутентификации, валидации, трансформации
  • Платёжные системы: обработка платежей через несколько уровней проверок

Выводы

Цепочка ответственности — это мощный паттерн для:

  • Разделения обработки запроса на независимые этапы
  • Создания гибких и расширяемых систем обработки
  • Реализации middleware и фильтров в веб-приложениях
  • Изоляции ответственности каждого обработчика

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

Для чего нужен паттерн цепочка ответственности? | PrepBro