Compartilhamento de tecnologia

Padrão de Design Padrão de Cadeia de Responsabilidade

2024-07-12

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

1. Conceito

Padrão de Cadeia de Responsabilidade: Evite acoplar o remetente e o destinatário da solicitação, dê a vários objetos a oportunidade de receber a solicitação, conecte esses objetos em uma cadeia e passe a solicitação ao longo dessa cadeia até que haja um objeto para tratá-la.O modelo da cadeia de responsabilidade é umpadrão de comportamento do objeto

2. Estrutura

O núcleo da estrutura do padrão da cadeia de responsabilidade é a introdução de umprocessador abstrato
Insira a descrição da imagem aqui

Como pode ser visto na figura, o diagrama de estrutura do padrão de cadeia de responsabilidade contém as duas funções a seguir:
(1) Manipulador (manipulador abstrato): define uma interface para processar solicitações e geralmente é projetado como uma classe abstrata. Como diferentes manipuladores concretos tratam as solicitações de maneira diferente, métodos abstratos de manipulação de solicitações são definidos neles.Como o subordinado de cada processador ainda é um processador, um objeto do tipo de processador abstrato (sucessor no diagrama de estrutura) é definido no processador abstrato como seu subordinado.Citar . Através desta referência, os manipuladores podem ser vinculados a uma cadeia.
(2) ConcreteHandler (manipulador concreto): é uma subclasse do manipulador abstrato e pode lidar com solicitações do usuário.O método de processamento de solicitação abstrata definido no processador abstrato é implementado na classe de processador concreta e pode acessar o próximo objeto na cadeia para alcançarSolicitar encaminhamentoEfeito.
No padrão Chain of Responsibility, as referências de cada objeto aos seus descendentes são conectadas para formar uma cadeia. As solicitações são passadas nesta cadeia até que o objeto da cadeia finalmente lide com a solicitação. Isso permite que o sistema reorganize dinamicamente a cadeia e atribua responsabilidades sem afetar o cliente.

3. Código típico

O núcleo do padrão de cadeia de responsabilidade está no design da classe manipuladora abstrata. O código típico da classe manipuladora abstrata é o seguinte:

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

No código acima, o processador abstrato define um objeto de referência para o próximo processador, a fim de encaminhar a solicitação para o próximo processador. O acessador deste objeto pode ser configurado como protegido, que pode ser utilizado em suas subclasses.

Manipuladores concretos são subclasses de manipuladores abstratos e têm duas funções principais:

  • Para processar solicitações, diferentes processadores específicos implementam o método abstrato de processamento de solicitações handleRequest() em diferentes formas;
  • Encaminhar a solicitação Se a solicitação exceder o intervalo de processamento do processador atual, a solicitação poderá ser encaminhada para o próximo processador.

O código típico para uma classe de manipulador específica é o seguinte:

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. Estudo de caso 1: Sistema de aprovação de pedidos de compra

Um determinado sistema de aprovação de pedidos de compras é hierárquico, ou seja, é aprovado por supervisores em diferentes níveis de acordo com o valor da compra.

O diretor pode aprovar pedidos de compra de menos de 50.000 yuans, o vice-presidente pode aprovar pedidos de compra de [5.100.000 yuans], o presidente do conselho pode aprovar pedidos de compra de [10.500.000 yuans] e pedidos de compra de 500.000 yuans e acima. Uma reunião do conselho é necessária para discutir e decidir.
Insira a descrição da imagem aqui

Como os aprovadores de cada cargo possuem subordinados (exceto o conselho de administração) e seus comportamentos são comuns, todos envolvem o encaminhamento de aprovações aos seus sucessores. Portanto, projete uma classe de aprovador como um processador abstrato:

//审批者类: 抽象处理者
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

Então cada posição, como processador específico, deve implementar um processador abstrato:

//主任: 具体处理者
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

Defina outra categoria de pedido de compra como o alvo que precisa ser aprovado:

//采购单: 请求类
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

Escreva o código de teste do cliente:

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

Compile e execute o programa, a saída será a seguinte:

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

5. Análise de Caso 2: Processamento de Tarefas de Classe

Vamos desenhar um caso um pouco diferente do anterior, que pode ampliar nossa compreensão do modelo de cadeia de responsabilidade e dos cenários em que ele pode ser aplicado.

  • A primeira diferença: o método de solicitação do manipulador tem um valor de retorno e pode fornecer informações ao host;
  • A segunda diferença: o caso "Sistema de aprovação de pedido de compra" processa a solicitação primeiro e depois encaminha a solicitação, enquanto o caso "Processamento de tarefa de classe" encaminha a solicitação primeiro e depois a processa.

A escola atribuirá algumas tarefas à turma para processamento. Esses tipos incluem um, dois, três, quatro, etc. O professor da turma pode realizar os três tipos de tarefas um, dois, três, e o monitor pode realizar os dois tipos de. um, dois. Tarefas, o comitê de aprendizagem pode lidar com um desse tipo de tarefa. Quando o professor da turma receber uma tarefa, ele primeiro a entregará ao líder do esquadrão para processamento. Se a próxima turma não conseguir lidar com ela, o professor da turma cuidará dela sozinho. Quando o professor da turma receber a tarefa, ele a entregará primeiro. entregue ao comitê escolar para processamento. Se a próxima turma não puder cuidar disso, o professor da turma cuidará disso sozinho quando o comitê escolar receber uma tarefa, ele mesmo cuidará disso, se puder; E quando não conseguirem dar conta, darão feedback para cima.

A classe Handler é projetada abaixo como um processador abstrato:
Como você e seus subordinados podem não ser necessariamente capazes de lidar com certas tarefas de classe e há feedback ascendente, o método de solicitação do processador precisa ter um valor de retorno booleano para informar ao superior se ele pode lidar com a tarefa.

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

Abaixo, o professor da turma, o líder do esquadrão e o membro do comitê de estudo são respectivamente designados como manipuladores específicos.
Por padrão, o professor da turma dará a tarefa primeiro ao monitor, e o monitor entregará a tarefa ao membro da comissão de estudo por padrão. O membro da comissão de estudo só pode realizar um tipo de tarefa e, se não puder, ela. retornará falso;
Se o feedback recebido pelo líder do esquadrão for falso, ele mesmo cuidará dele. Ele só poderá realizar um ou dois tipos de tarefas. Se não conseguir realizar isso, ele retornará falso;
Se o feedback recebido pelo diretor for falso, ele mesmo cuidará dele. Ele só poderá realizar tarefas de um, dois e três tipos. Se não conseguir realizar as tarefas, ele retornará falso.

//班主任
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

Escreva o código de teste do cliente:
Configure subcasas para cada cargo, mas observe que não há subcasas para membros do comitê de estudo.
Além disso, se houver uma tarefa que o diretor não consiga realizar, imprima uma linha de registro para explicá-la.

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

Compile e execute o programa, a saída será a seguinte:

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

6. Cenários aplicáveis

O modelo Cadeia de Responsabilidade pode ser considerado nas seguintes situações:

  • Vários objetos podem tratar a mesma solicitação. O objeto específico que trata a solicitação será determinado em tempo de execução.O cliente só precisa enviar a solicitação à rede, sem se preocupar com quem e como a solicitação é processada.
  • Envie uma solicitação para um dos vários objetos sem especificar explicitamente o destinatário
  • Um conjunto de objetos pode ser especificado dinamicamente para lidar com solicitações.O cliente pode criar dinamicamente uma cadeia de responsabilidades para lidar com solicitações e também pode alterar a ordem entre os processadores na cadeia.


Livros de referência:
"A arte dos padrões de design" - Liu Wei