Обмен технологиями

Шаблон проектирования. Шаблон цепочки ответственности.

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Концепция

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

2. Структура

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

Как видно из рисунка, структурная схема шаблона цепочки ответственности содержит следующие две роли:
(1) Обработчик (абстрактный обработчик): определяет интерфейс для обработки запросов и обычно разрабатывается как абстрактный класс. Поскольку разные конкретные обработчики обрабатывают запросы по-разному, в них определены абстрактные методы обработки запросов.Поскольку подчиненный каждый процессор по-прежнему является процессором, объект типа абстрактного процессора (преемник на структурной диаграмме) определяется в абстрактном процессоре как его подчиненный.Цитировать . Посредством этой ссылки обработчики могут быть связаны в цепочку.
(2) ConcreteHandler (конкретный обработчик): это подкласс абстрактного обработчика, который может обрабатывать запросы пользователей.Метод обработки абстрактного запроса, определенный в абстрактном процессоре, реализован в конкретном классе процессора и может получить доступ к следующему объекту в цепочке для достиженияЗапросить переадресациюЭффект.
В шаблоне «Цепочка ответственности» ссылки каждого объекта на его потомков соединяются, образуя цепочку. Запросы передаются вверх по этой цепочке до тех пор, пока какой-либо объект в цепочке наконец не обработает запрос. Это позволяет системе динамически реорганизовывать цепочку и распределять обязанности, не затрагивая клиента.

3. Типичный код

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

abstract class Handler {
    //维持对下家的引用
    protected Handler successor;
    
    public void setSuccessor(Handler successor) {
        this.successor = successor;    
    }
    
    public abstract void handleRequest(String request);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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

Конкретные обработчики являются подклассами абстрактных обработчиков и имеют две основные функции:

  • Для обработки запросов разные конкретные процессоры реализуют абстрактный метод обработки запросов handleRequest() в разных формах;
  • Переслать запрос. Если запрос превышает диапазон обработки текущего процессора, запрос может быть перенаправлен следующему процессору.

Типичный код для конкретного класса обработчика выглядит следующим образом:

class ConcreteHandler extends Handler {
    public void handleRequest(String request) {
        if (请求满足条件) {
            //处理请求        
        } 
        else {
            this.successor.handleRequest(request);  //转发请求
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

4. Пример 1: Система утверждения заказов на поставку

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

Директор может утверждать заказы на покупку на сумму менее 50 000 юаней, заместитель председателя может утверждать заказы на покупку на сумму [5 100 000 юаней], председатель правления может утверждать заказы на покупку на сумму [10 500 000 юаней] и заказы на покупку на сумму 500 000 юаней. и выше необходимо собрание совета директоров для обсуждения и принятия решения.
Вставьте сюда описание изображения

Поскольку у утверждающих каждую должность есть подчиненные (кроме совета директоров) и их поведение является общим, все они предполагают пересылку одобрений своим преемникам. Поэтому спроектируйте класс утверждающего как абстрактный процессор:

//审批者类: 抽象处理者
abstract class Approver {
    protected Approver successor;  //定义后继对象
    protected String name;  //审批者姓名

    public Approver(String name) {
        this.name = name;
    }

    //设置后继者
    public void setSuccessor(Approver successor) {
        this.successor = successor;
    }

    public abstract void processRequest(PurchaseRequest request);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

Тогда каждая позиция, как конкретный процессор, должна реализовать абстрактный процессор:

//主任: 具体处理者
class Director extends Approver{
    public Director(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 50000) {
            System.out.println(
                    MessageFormat.format("主任 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",
                            this.name, request.getNumber(), request.getAmount(), request.getPurpose())
            );
        } else {
            this.successor.processRequest(request);  //转发请求
        }
    }
}

//副董事长:具体处理类
class VicePresident extends Approver{
    public VicePresident(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 100000) {
            System.out.println(
                    MessageFormat.format("副董事长 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",
                            this.name, request.getNumber(), request.getAmount(), request.getPurpose())
            );
        } else {
            this.successor.processRequest(request);
        }
    }
}

//董事长类:具体处理者
class President extends Approver{
    public President(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        if (request.getAmount() < 500000) {
            System.out.println(
                    MessageFormat.format("董事长 {0} 审批采购单:{1}, 金额:{2}元, 采购目的:{3}。",
                            this.name, request.getNumber(), request.getAmount(), request.getPurpose())
            );
        } else {
            this.successor.processRequest(request);
        }
    }
}

//董事会类:具体处理者
class Congress extends Approver{
    public Congress(String name) {
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest request) {
        System.out.println(
                MessageFormat.format("召开董事会 审批采购单:{0}, 金额:{1}元, 采购目的:{2}。",
                        request.getNumber(), request.getAmount(), request.getPurpose())
        );
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

Определите другую категорию заказа на покупку в качестве цели, которую необходимо утвердить:

//采购单: 请求类
class PurchaseRequest {
    private double amount;  //采购金额
    private int number;  //采购单编号
    private String purpose;  //采购目的

    public PurchaseRequest(double amount, int number, String purpose) {
        this.amount = amount;
        this.number = number;
        this.purpose = purpose;
    }

    public double getAmount() {
        return amount;
    }

    public void setAmount(double amount) {
        this.amount = amount;
    }

    public int getNumber() {
        return number;
    }

    public void setNumber(int number) {
        this.number = number;
    }

    public String getPurpose() {
        return purpose;
    }

    public void setPurpose(String purpose) {
        this.purpose = purpose;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

Напишите тестовый код клиента:

class Client {
    public static void main(String[] args) {
        Approver kangXi, yongZheng, qianLong, hanLinYuan;
        kangXi = new Director("康熙");
        yongZheng = new VicePresident("雍正");
        qianLong = new VicePresident("乾隆");
        hanLinYuan = new Congress("翰林院");

        //创建职责链
        kangXi.setSuccessor(yongZheng);
        yongZheng.setSuccessor(qianLong);
        qianLong.setSuccessor(hanLinYuan);

        //创建采购单
        PurchaseRequest pr1 = new PurchaseRequest(45000, 10001, "购买刀");
        kangXi.processRequest(pr1);

        PurchaseRequest pr2 = new PurchaseRequest(60000, 10002, "购买枪");
        kangXi.processRequest(pr2);

        PurchaseRequest pr3 = new PurchaseRequest(160000, 10003, "购买火炮");
        kangXi.processRequest(pr3);

        PurchaseRequest pr4 = new PurchaseRequest(800000, 10004, "购买军舰");
        kangXi.processRequest(pr4);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

Скомпилируйте и запустите программу, результат будет следующий:

主任 康熙 审批采购单:10,001, 金额:45,000元, 采购目的:购买刀。
副董事长 雍正 审批采购单:10,002, 金额:60,000元, 采购目的:购买枪。
董事长 乾隆 审批采购单:10,003, 金额:160,000元, 采购目的:购买火炮。
召开董事会 审批采购单:10,004, 金额:800,000元, 采购目的:购买军舰。
  • 1
  • 2
  • 3
  • 4

5. Анализ случая 2: Обработка задач класса

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

  • Первое отличие: метод запроса обработчика имеет возвращаемое значение и может возвращать информацию хосту;
  • Второе отличие: случай «Система утверждения заказов на закупку» сначала обрабатывает запрос, а затем пересылает запрос, а случай «Обработка классовых задач» сначала пересылает запрос, а затем обрабатывает запрос.

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

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

abstract class Handler {
    protected Handler successor;  //定义后继对象

    public void setSuccessor(Handler successor) {
        this.successor = successor;
    }

    public abstract boolean handleRequest(String taskName);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Ниже классный руководитель, руководитель отряда и член учебного комитета соответственно задуманы как конкретные кураторы.
По умолчанию классный руководитель сначала дает задание монитору, а монитор по умолчанию передает задание члену учебного комитета. Член учебного комитета может справиться только с одним типом задач, а если он не может справиться, то он. вернет ложь;
Если отзыв, полученный командиром отряда, ложный, он справится с ним сам. Он может справиться только с одним или двумя типами задач. Если он не сможет справиться с этим, он вернет false;
Если полученная директором обратная связь ложна, он справится с ней сам. Он может справиться только с заданиями одного, двух и трех типов. Если он не сможет справиться с заданиями, он вернет ложь.

//班主任
class HeadTeacher extends Handler{
    @Override
    public boolean handleRequest(String taskName) {
        boolean handled = successor.handleRequest(taskName);
        if (handled) {
            return true;
        }
        if (taskName.equals("one") || taskName.equals("two") || taskName.equals("three")) {
            System.out.println("班主任处理了该事务");
            return true;
        }

        return false;
    }
}

//班长
class Monitor extends Handler{
    @Override
    public boolean handleRequest(String taskName) {
        boolean handled = successor.handleRequest(taskName);
        if (handled) {
            return true;
        }
        if (taskName.equals("one") || taskName.equals("two")) {
            System.out.println("班长处理了该事务");
            return true;
        }

        return false;
    }
}

//学习委员
class StudyCommissary extends Handler{
    @Override
    public boolean handleRequest(String taskName) {
        boolean handled;
        if (successor == null) {  //注意学习委员可能没有下家,所以这里判一下是否为空
            handled = false;
        } else {
            handled = successor.handleRequest(taskName);
        }

        if (handled) {
            return true;
        }
        if (taskName.equals("one")) {
            System.out.println("学习委员处理了该事务");
            return true;
        }

        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

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

public class SchoolClient {
    public static void main(String[] args) {
        Handler headTeacher, monitor, studyCommissary;
        headTeacher = new HeadTeacher();
        monitor = new Monitor();
        studyCommissary = new StudyCommissary();

        headTeacher.setSuccessor(monitor);
        monitor.setSuccessor(studyCommissary);
        studyCommissary.setSuccessor(null);  //没有下一职责人

        startRequest(headTeacher, "one");
        startRequest(headTeacher, "two");
        startRequest(headTeacher, "three");
        startRequest(headTeacher, "four");
    }

    private static void startRequest(Handler headTeacher, String taskName) {
        if (! headTeacher.handleRequest(taskName)) {
            System.out.println("该班级处理不了此类任务!");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Скомпилируйте и запустите программу, результат будет следующий:

学习委员处理了该事务
班长处理了该事务
班主任处理了该事务
该班级处理不了此类任务!
  • 1
  • 2
  • 3
  • 4

6. Применимые сценарии

Модель цепочки ответственности может рассматриваться в следующих ситуациях:

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


Справочная литература:
«Искусство проектирования шаблонов» — Лю Вэй