Compartir tecnología

Patrón de diseño Patrón de cadena de responsabilidad

2024-07-12

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

1. Concepto

Patrón de cadena de responsabilidad: evite acoplar el remitente y el receptor de la solicitud, brinde a varios objetos la oportunidad de recibir la solicitud, conecte estos objetos en una cadena y pase la solicitud a lo largo de esta cadena hasta que haya un objeto para manejarla.El modelo de cadena de responsabilidad es unpatrón de comportamiento del objeto

2. Estructura

El núcleo de la estructura del patrón de cadena de responsabilidad es la introducción de unprocesador abstracto
Insertar descripción de la imagen aquí

Como se puede ver en la figura, el diagrama de estructura del patrón de cadena de responsabilidad contiene los dos roles siguientes:
(1) Controlador (controlador abstracto): define una interfaz para procesar solicitudes y generalmente está diseñado como una clase abstracta. Debido a que diferentes controladores concretos manejan las solicitudes de manera diferente, en ellos se definen métodos abstractos de manejo de solicitudes.Debido a que el subordinado de cada procesador sigue siendo un procesador, un objeto de tipo de procesador abstracto (sucesor en el diagrama de estructura) se define en el procesador abstracto como su subordinado.Cita . A través de esta referencia, los manipuladores se pueden vincular en una cadena.
(2) ConcreteHandler (manejador concreto): es una subclase de manejador abstracto y puede manejar solicitudes de usuarios.El método de procesamiento de solicitudes abstractas definido en el procesador abstracto se implementa en la clase de procesador concreta y puede acceder al siguiente objeto de la cadena para lograrSolicitar reenvíoEfecto.
En el patrón Cadena de Responsabilidad, las referencias de cada objeto a sus descendientes están conectadas para formar una cadena. Las solicitudes pasan por esta cadena hasta qué objeto de la cadena finalmente maneja la solicitud. Esto permite que el sistema reorganice dinámicamente la cadena y asigne responsabilidades sin afectar al cliente.

3.Código típico

El núcleo del patrón de cadena de responsabilidad radica en el diseño de la clase de controlador abstracto. El código típico de la clase de controlador abstracto es el siguiente:

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

En el código anterior, el procesador abstracto define un objeto de referencia al siguiente procesador para reenviar la solicitud al siguiente procesador. El descriptor de acceso de este objeto se puede configurar como protegido, lo que se puede usar en sus subclases.

Los manejadores concretos son subclases de manejadores abstractos y tienen dos funciones principales:

  • Para procesar solicitudes, diferentes procesadores específicos implementan el método de procesamiento de solicitudes abstractas handleRequest() en diferentes formas;
  • Reenviar la solicitud. Si la solicitud excede el rango de procesamiento del procesador actual, la solicitud se puede reenviar al siguiente procesador.

El código típico para una clase de controlador específica es el siguiente:

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. Estudio de caso 1: Sistema de aprobación de órdenes de compra

Un determinado sistema de aprobación de órdenes de compra es jerárquico, es decir, es aprobado por supervisores en diferentes niveles según el monto de la compra.

El director puede aprobar órdenes de compra de menos de 50.000 yuanes, el vicepresidente puede aprobar órdenes de compra de [5.100.000 yuanes], el presidente de la junta puede aprobar órdenes de compra de [10.500.000 yuanes] y órdenes de compra de 500.000 yuanes y superiores. Se necesita una reunión de la junta directiva para discutir y decidir.
Insertar descripción de la imagen aquí

Dado que los aprobadores de cada puesto tienen subordinados (excepto la junta directiva) y sus comportamientos son comunes, todos implican remitir aprobaciones a sus sucesores. Así que diseñe una clase de aprobador como un procesador abstracto:

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

Entonces cada puesto, como procesador específico, debe implementar un procesador abstracto:

//主任: 具体处理者
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 otra categoría de orden de compra como el objetivo que debe aprobarse:

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

Escriba el código de prueba del 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 y ejecute el programa, el resultado es el siguiente:

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

5. Análisis de caso 2: Procesamiento de tareas de clase

Diseñemos un caso ligeramente diferente al anterior, que pueda ampliar nuestra comprensión del modelo de cadena de responsabilidad y los escenarios en los que se puede aplicar.

  • La primera diferencia: el método de solicitud del controlador tiene un valor de retorno y puede enviar información al host;
  • La segunda diferencia: el caso "Sistema de aprobación de órdenes de compra" procesa la solicitud primero y luego la reenvía, mientras que el caso "Procesamiento de tareas de clase" reenvía la solicitud primero y luego la procesa.

La escuela asignará algunas tareas a la clase para su procesamiento. Estos tipos incluyen uno, dos, tres, cuatro, etc. El maestro de la clase puede manejar los tres tipos de tareas uno, dos, tres, y el monitor puede manejar los dos tipos de. una, dos tareas, el comité de aprendizaje puede manejar una de este tipo de tareas. Cuando el maestro de la clase recibe una tarea, primero se la entregará al líder del escuadrón para que la procese. Si la siguiente clase no puede manejarla, el maestro de la clase la entregará él mismo. al comité escolar para su procesamiento. Si la siguiente clase no puede manejarla, el maestro de la clase lo hará. El líder del escuadrón se encargará él mismo cuando el comité escolar reciba una tarea, él mismo la manejará si puede. Y cuando no puedan manejarlo, darán retroalimentación hacia arriba.

La clase Handler se diseña a continuación como un procesador abstracto:
Dado que es posible que usted y sus subordinados no necesariamente puedan manejar ciertas tareas de clase y hay retroalimentación ascendente, el método de solicitud del procesador debe tener un valor de retorno booleano para indicarle al superior si puede manejar la tarea.

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

A continuación, el maestro de la clase, el líder del escuadrón y el miembro del comité de estudio están diseñados respectivamente como manejadores específicos.
De forma predeterminada, el maestro de la clase le dará la tarea al monitor primero, y el monitor le dará la tarea al miembro del comité de estudio de manera predeterminada. El miembro del comité de estudio solo puede manejar un tipo de tareas, y si no puede manejarlas, puede hacerlo. devolverá falso;
Si los comentarios recibidos por el líder del escuadrón son falsos, él mismo los manejará. Solo puede manejar uno o dos tipos de tareas. Si no puede manejarlos, devolverá falso;
Si los comentarios recibidos por el director son falsos, él mismo los manejará. Solo puede manejar tareas de uno, dos y tres tipos. Si no puede manejar las tareas, devolverá 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

Escriba el código de prueba del cliente:
Establezca subcámaras para cada puesto, pero tenga en cuenta que no hay subcámaras para los miembros del comité de estudio.
Además, si hay una tarea que el director no puede realizar, imprima una línea de registro para explicarla.

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 y ejecute el programa, el resultado es el siguiente:

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

6. Escenarios aplicables

El modelo de Cadena de Responsabilidad se puede considerar en las siguientes situaciones:

  • Varios objetos pueden manejar la misma solicitud. El objeto específico que maneja la solicitud se determinará en tiempo de ejecución.El cliente solo necesita enviar la solicitud a la cadena sin importar quién procesa la solicitud y cómo se procesa.
  • Enviar una solicitud a uno de varios objetos sin especificar explícitamente el destinatario
  • Se puede especificar dinámicamente un conjunto de objetos para manejar solicitudes.El cliente puede crear dinámicamente una cadena de responsabilidades para manejar las solicitudes y también puede cambiar el orden entre los procesadores de la cadena.


Libros de referencia:
"El arte de los patrones de diseño" - Liu Wei