Разработка перехватчика с использованием шаблона цепочки ответственности

задняя часть GitHub Шаблоны проектирования
Разработка перехватчика с использованием шаблона цепочки ответственности

предисловие

делаю это недавноCicadaФункция перехватчика просто использует шаблон цепочки ответственности.

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

Модель цепочки ответственности

Давайте посмотрим, что такое паттерн «Цепочка ответственности».

Цитируя объяснение из Википедии:

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

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

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

Применение модели цепочки ответственности

традиционная реализация

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

Обычно пишут так:

public class Main {
    public static void main(String[] args) {
        String msg = "内容内容内容" ;

        String result = Process.sensitiveWord()
                .typo()
                .copyright();
    }
}

Кажется, это без проблем решает проблему, но что, если мне еще нужно добавить к контенту унифицированный заголовок? В существующий способ приходится добавлять новый метод обработки, и он есть в этом клиенте (Process) в зависимости от доп.

Очевидно, что такая масштабируемость не является хорошей.

Реализация шаблона цепочки ответственности

Здесь в игру вступает модель цепочки ответственности.

Это требование очень хорошо согласуется с характеристиками серии обработки объекта и запроса.

Итак, модифицируем код:

В настоящее времяProcessЭто интерфейс, используемый для определения реальной функции обработки.

public interface Process {

    /**
     * 执行处理
     * @param msg
     */
    void doProcess(String msg) ;
}

При этом вся предыдущая обработка контента нуждается только в реализации этого интерфейса:

public class SensitiveWordProcess implements Process {
    @Override
    public void doProcess(String msg) {
        System.out.println(msg + "敏感词处理");
    }
}

public class CopyrightProcess implements Process {

    @Override
    public void doProcess(String msg) {
        System.out.println(msg + "版权处理");
    }
}

public class CopyrightProcess implements Process {

    @Override
    public void doProcess(String msg) {
        System.out.println(msg + "版权处理");
    }
}

Затем вам нужно только предоставить клиенту запись об исполнении и запись для добавления цепочки ответственности:

public class MsgProcessChain {

    private List<Process> chains = new ArrayList<>() ;

    /**
     * 添加责任链
     * @param process
     * @return
     */
    public MsgProcessChain addChain(Process process){
        chains.add(process) ;
        return this ;
    }

    /**
     * 执行处理
     * @param msg
     */
    public void process(String msg){
        for (Process chain : chains) {
            chain.doProcess(msg);
        }
    }
}

Это очень просто использовать:

public class Main {
    public static void main(String[] args) {
        String msg = "内容内容内容==" ;

        MsgProcessChain chain = new MsgProcessChain()
                .addChain(new SensitiveWordProcess())
                .addChain(new TypoProcess())
                .addChain(new CopyrightProcess()) ;

        chain.process(msg) ;
    }
}

Когда мне нужно добавить другую логику обработки, мне нужно добавить только блок обработки (addChain(Process process)), а клиентуchain.process(msg)Он незаметен и не требует никаких изменений.

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

НапримерNettyсерединаpipelineЭто типичный шаблон цепочки ответственности, который позволяет запросу проходить через весь конвейер.

Из официальной схемы ясно видно, что это модель цепочки ответственности:

Разработайте перехватчик с помощью шаблона цепочки ответственности

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

Давайте посмотрим наCicadaРеализация в:

Во-первых, определить и вышеProcessаналогичный интерфейсCicadaInterceptorАбстрактный класс:

public abstract class CicadaInterceptor {

    public boolean before(CicadaContext context,Param param) throws Exception{
        return true;
    }

    public void after(CicadaContext context,Param param) throws Exception{}
}

также определяетInterceptProcessклиент:

один из нихloadInterceptors()Все перехватчики будут добавлены в цепочку ответственности.

Предусмотрены еще две функции, соответствующие записи до и после перехвата:

практическое применение

Теперь давайте посмотрим, как он используется.

по запросуhandleзагрузить сначала(loadInterceptors(AppConfig appConfig)), то есть инициализировать цепочку ответственности.

Далее идет вход клиента, может вызываться метод входа до и после перехвата.

Так как это перехватчик, то вbeforeЗапрос может быть перехвачен в функции. просто вернисьfalseОн не будет продолжать обработку в обратном направлении. Итак, вот оценка возвращаемого значения.

В то же время пользователям нужно только создать наследование класса перехватчикаCicadaInterceptorкласс подойдет.

Вот демо с двумя перехватчиками:

  1. записывать бизнесhandleвремя исполнения.
  2. существуетafterПараметры запроса распечатываются.
  3. При этом можно вернуться в первый перехватчикfalseПусть запрос будет перехвачен.

Сначала проведем первые два теста:


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


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


В интерфейсе запроса можно увидеть следующее:

При этом следующие параметры запроса не выводятся, что говорит о том, что запрос действительно перехвачен.


При этом я тоже могу настроить порядок перехвата, просто нужно@Interceptor(order = 1)определите это в аннотацииorderсвойство (значение по умолчанию равно 0, чем меньше значение, тем выполняется первым).

Раньше перехватчик, выводивший параметры запроса, выполнялся первым, в этот раз я вручную настроил его порядок на 2, а порядок времени печати на 1.

Снова запросите интерфейс, чтобы просмотреть фоновый журнал:

Перехватчик, который находит время выполнения печати, выполняется первым.

Как этот порядок выполнения реализует пользовательскую конфигурацию?

На самом деле, это относительно просто, со следующими шагами:

  • При загрузке перехватчика аннотация будетorderсохрани это.
  • При постановке перехватчика в цепочку ответственности отражение будетorderЗначение сохраняется для каждого перехватчика.
  • Наконец, измените порядок этой цепочки ответственности путем сортировки.

Опубликуйте основной код.

Сохранить при сканировании перехватчиковorderценность:


спастиorderзначение в перехватчик:


Переупорядочить цепочку ответственности:

Суммировать

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

Исходный код выше выглядит следующим образом:

Добро пожаловать, чтобы обратить внимание на публичный аккаунт, чтобы общаться вместе: