기술나눔

디자인 패턴 책임 사슬 패턴

2024-07-12

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

1. 컨셉

책임 체인 패턴: 요청 발신자와 수신자를 연결하지 않고, 여러 개체에 요청을 받을 수 있는 기회를 제공하고, 이러한 개체를 체인으로 연결하고, 처리할 개체가 있을 때까지 이 체인을 따라 요청을 전달합니다.책임 사슬 모델은객체 행동 패턴

2. 구조

책임 사슬 패턴 구조의 핵심은추상 프로세서
여기에 이미지 설명을 삽입하세요.

그림에서 볼 수 있듯이 책임 체인 패턴 구조 다이어그램에는 다음 두 가지 역할이 포함됩니다.
(1) 핸들러(추상 핸들러): 요청을 처리하기 위한 인터페이스를 정의하며 일반적으로 추상 클래스로 설계된다. 서로 다른 구체적인 핸들러는 요청을 다르게 처리하기 때문에 추상 요청 처리 방법이 정의됩니다.각 프로세서의 하위 프로세서는 여전히 프로세서이기 때문에 추상 프로세서 유형의 개체(구조 다이어그램의 후속자)는 추상 프로세서에서 하위 프로세서로 정의됩니다.인용하다 . 이 참조를 통해 핸들러를 체인으로 연결할 수 있습니다.
(2) ConcreteHandler(콘크리트 핸들러): 추상 핸들러의 하위 클래스로 사용자 요청을 처리할 수 있습니다.추상 프로세서에서 정의된 추상 요청 처리 방법은 구체적인 프로세서 클래스에서 구현되며 체인의 다음 개체에 액세스하여 달성할 수 있습니다.요청 전달효과.
Chain of Responsibility 패턴에서는 각 개체의 하위 항목에 대한 참조가 연결되어 체인을 형성합니다. 요청은 체인의 어느 개체가 최종적으로 요청을 처리할 때까지 이 체인으로 전달됩니다. 이를 통해 시스템은 클라이언트에 영향을 주지 않고 체인을 동적으로 재구성하고 책임을 할당할 수 있습니다.

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

위 코드에서 추상 프로세서는 요청을 다음 프로세서로 전달하기 위해 다음 프로세서에 참조 개체를 정의합니다. 이 객체의 접근자는 보호로 설정될 수 있으며, 이는 하위 클래스에서 사용할 수 있습니다.

구상 처리기는 추상 처리기의 하위 클래스이며 두 가지 주요 기능을 갖습니다.

  • 요청을 처리하기 위해 다양한 특정 프로세서는 추상 요청 처리 메서드인 handlerRequest()를 다양한 형식으로 구현합니다.
  • 요청을 전달합니다. 요청이 현재 프로세서의 처리 범위를 초과하는 경우 해당 요청은 다음 프로세서로 전달될 수 있습니다.

특정 핸들러 클래스의 일반적인 코드는 다음과 같습니다.

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: 수업과제 처리

책임 사슬 모델과 이를 적용할 수 있는 시나리오에 대한 이해를 넓힐 수 있도록 위와 약간 다른 사례를 설계해 보겠습니다.

  • 첫 번째 차이점: 핸들러의 요청 메소드에는 반환 값이 있으며 호스트에 정보를 피드백할 수 있습니다.
  • 두 번째 차이점: "구매 주문 승인 시스템" 사례는 요청을 먼저 처리한 후 요청을 전달하는 반면, "클래스 작업 처리" 사례는 요청을 먼저 전달한 후 요청을 처리합니다.

학교는 처리를 위해 학급에 몇 가지 작업을 할당합니다. 이러한 유형에는 1, 2, 3, 4 등이 포함됩니다. 담임 교사는 1, 2, 3의 세 가지 유형의 작업을 처리할 수 있으며 모니터는 두 가지 유형의 작업을 처리할 수 있습니다. 하나, 둘. 학습위원회는 이러한 유형의 작업 중 하나를 처리할 수 있습니다. 담임 선생님이 과제를 받으면 먼저 분대장에게 넘겨 처리하게 하고, 다음 반이 처리하지 못할 경우 담임 선생님이 과제를 받으면 먼저 처리하게 됩니다. 처리를 위해 학교 위원회에 넘깁니다. 다음 학급에서 처리할 수 없는 경우에는 담당 교사가 처리합니다. 학교 위원회에서 작업을 받으면 분대장이 직접 처리합니다. 그리고 감당할 수 없을 때는 위쪽으로 피드백을 줍니다.

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를 반환합니다.
분대장이 받은 피드백이 거짓이면 그는 스스로 처리할 것입니다. 그가 처리할 수 없는 경우에는 한두 가지 유형의 작업만 처리할 수 있습니다.
교장 선생님이 받은 피드백이 거짓이면 그는 1, 2, 3가지 유형의 작업만 처리할 수 있습니다.

//班主任
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. 적용 가능한 시나리오

책임 사슬 모델은 다음과 같은 상황에서 고려될 수 있습니다.

  • 동일한 요청을 처리할 수 있는 개체가 여러 개 있습니다. 요청을 처리하는 특정 개체는 런타임에 결정됩니다.클라이언트는 요청이 누구에 의해 처리되고 어떻게 처리되는지 신경 쓰지 않고 체인에 요청을 제출하기만 하면 됩니다.
  • 수신자를 명시적으로 지정하지 않고 여러 개체 중 하나에 요청을 제출합니다.
  • 요청을 처리하기 위해 개체 집합을 동적으로 지정할 수 있습니다.클라이언트는 요청을 처리하기 위해 책임 체인을 동적으로 생성할 수 있으며 체인에 있는 프로세서 간의 순서를 변경할 수도 있습니다.


참고 도서:
"디자인 패턴의 예술" - Liu Wei