«1. Введение

В этой статье мы рассмотрим широко используемый шаблон поведенческого проектирования: цепочка ответственности.

Мы можем найти больше шаблонов проектирования в нашей предыдущей статье.

2. Цепочка ответственности

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

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

Шаблон цепочки ответственности удобен для:

    Разделения отправителя и получателя команды Выбора стратегии обработки во время обработки

Итак, давайте рассмотрим простой пример шаблона.

3. Пример

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

Таким образом, входной провайдер аутентификации будет командой, а каждый процессор аутентификации будет отдельным объектом-процессором.

Давайте сначала создадим абстрактный базовый класс для наших процессоров:

public abstract class AuthenticationProcessor {

    public AuthenticationProcessor nextProcessor;
    
    // standard constructors

    public abstract boolean isAuthorized(AuthenticationProvider authProvider);
}

Затем давайте создадим конкретные процессоры, расширяющие AuthenticationProcessor:

public class OAuthProcessor extends AuthenticationProcessor {

    public OAuthProcessor(AuthenticationProcessor nextProcessor) {
        super(nextProcessor);
    }

    @Override
    public boolean isAuthorized(AuthenticationProvider authProvider) {
        if (authProvider instanceof OAuthTokenProvider) {
            return true;
        } else if (nextProcessor != null) {
            return nextProcessor.isAuthorized(authProvider);
        }
        
        return false;
    }
}
public class UsernamePasswordProcessor extends AuthenticationProcessor {

    public UsernamePasswordProcessor(AuthenticationProcessor nextProcessor) {
        super(nextProcessor);
    }

    @Override
    public boolean isAuthorized(AuthenticationProvider authProvider) {
        if (authProvider instanceof UsernamePasswordProvider) {
            return true;
        } else if (nextProcessor != null) {
            return nextProcessor.isAuthorized(authProvider);
        }
    return false;
    }
}

Здесь мы создали два конкретных процессора для нашей входящей авторизации. запросы: UsernamePasswordProcessor и OAuthProcessor.

Для каждого из них мы переопределяем метод isAuthorized.

public class ChainOfResponsibilityTest {

    private static AuthenticationProcessor getChainOfAuthProcessor() {
        AuthenticationProcessor oAuthProcessor = new OAuthProcessor(null);
        return new UsernamePasswordProcessor(oAuthProcessor);
    }

    @Test
    public void givenOAuthProvider_whenCheckingAuthorized_thenSuccess() {
        AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
        assertTrue(authProcessorChain.isAuthorized(new OAuthTokenProvider()));
    }

    @Test
    public void givenSamlProvider_whenCheckingAuthorized_thenSuccess() {
        AuthenticationProcessor authProcessorChain = getChainOfAuthProcessor();
 
        assertFalse(authProcessorChain.isAuthorized(new SamlTokenProvider()));
    }
}

Теперь давайте создадим пару тестов:

В приведенном выше примере создается цепочка процессоров аутентификации: UsernamePasswordProcessor -\u003e OAuthProcessor. В первом тесте авторизация прошла успешно, а в другом — нет.

Сначала UsernamePasswordProcessor проверяет, является ли поставщик аутентификации экземпляром UsernamePasswordProvider.

Не являясь ожидаемым вводом, UsernamePasswordProcessor делегирует OAuthProcessor.

Наконец, OAuthProcessor обрабатывает команду. В первом тесте есть совпадение, и тест проходит. Во втором в цепочке больше нет процессоров, и, как следствие, тест проваливается.

4. Принципы реализации

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

Каждый процессор в цепочке будет иметь свою реализацию для обработки команды. реализация isAuthorized Каждый процессор в цепочке должен иметь ссылку на следующий процессор Выше, UsernamePasswordProcessor делегирует OAuthProcessor Каждый процессор отвечает за делегирование следующему процессору, поэтому остерегайтесь отброшенных команд. Опять же, в нашем примере, если команда является экземпляром SamlProvider, тогда запрос может не пройти обработку и будет несанкционированным. Процессоры не должны формировать рекурсивный цикл. В нашем примере у нас нет цикла в нашей цепочке: UsernamePasswordProcessor -\u003e OAuthProcessor. Но если мы явно укажем UsernamePasswordProcessor в качестве следующего обработчика OAuthProcessor, то получим цикл в нашей цепочке: UsernamePasswordProcessor -\u003e OAuthProcessor -\u003e UsernamePasswordProcessor. В этом может помочь выбор следующего процессора в конструкторе Только один процессор в цепочке обрабатывает данную команду. В нашем примере, если входящая команда содержит экземпляр OAuthTokenProvider, то только OAuthProcessor будет обрабатывать команду

5. Использование в Реальный мир

В мире Java мы каждый день извлекаем выгоду из цепочки ответственности. Одним из таких классических примеров являются фильтры сервлетов в Java, которые позволяют нескольким фильтрам обрабатывать HTTP-запрос. Хотя в этом случае каждый фильтр вызывает цепочку вместо следующего фильтра.

public class CustomFilter implements Filter {

    public void doFilter(
      ServletRequest request,
      ServletResponse response,
      FilterChain chain)
      throws IOException, ServletException {

        // process the request

        // pass the request (i.e. the command) along the filter chain
        chain.doFilter(request, response);
    }
}

Давайте взглянем на фрагмент кода ниже, чтобы лучше понять этот шаблон в фильтрах сервлетов:

Как видно из приведенного выше фрагмента кода, нам нужно вызвать метод doFilter в FilterChain, чтобы передать запрос к следующему процессору в цепочке.

6. Недостатки

    И теперь, когда мы увидели, насколько интересна цепочка ответственности, давайте вспомним о некоторых недостатках:

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

7. Заключение

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