디자인 패턴 - Java의 책임 사슬 패턴 예 - 조달 승인 시스템

장면

구매 주문 승인

조달 승인은 여러 수준에서 수행됩니다. 즉, 구매 금액의 차이에 따라 여러 수준의 감독자의 승인을 받습니다.

부회장은 5만~10만위안(10만위안 제외)의 구매주문을 승인할 수 있고, 의장은 10만~50만위안(50만위안 제외)의 구매주문을 승인할 수 있다.

500,000위안 이상의 구매 주문은 이사회에서 논의하고 결정해야 합니다.

Chain of Responsibility 패턴을 구매하지 않은 경우 다음과 같이 구현될 수 있습니다.

public class PurchaseRequestHandler {
    public void sendRequestToDirector(int amount){
        if(amount<5000){
            this.handleByDirector();
        }else if(amount<10000){
            this.handleByVicePresident();
        }else if(amount<50000){
            this.handleByPresident();
        }else {
            this.handleByCongress();
        }
    }

    //主任审批采购单
    public void handleByDirector(){
        //代码省略
    }

    //副董事长审批采购单
    public void handleByVicePresident(){
        //代码省略
    }

    //董事长审批采购单
    public void handleByPresident(){
        //代码省略
    }

    //董事会审批采购单
    public void handleByCongress(){
        //代码省略
    }

}

문제가 있습니다

(1) BuyRequestHandler 클래스는 상대적으로 크고, 각 레벨의 승인 방법이 하나의 클래스에 집중되어 있어 "단일 책임 원칙"을 위반합니다.

테스트 및 유지 관리가 어렵습니다.

(2) 새로운 승인 등급을 추가하거나 각 등급의 승인 금액 및 승인 내용을 조정해야 하는 경우(예: 의장 승인 금액을 60만 위안으로 변경)

소스코드를 수정하고 엄격한 테스트를 거쳐야 하며, 또한 일정 수준을 제거해야 하는 경우(예를 들어 10만 위안 이상의 구매 주문은 회장이 직접 승인한 경우,

부회장 직위가 없어지면 소스코드도 수정해야 하는데, 이는 '개방 및 비공개 원칙'에 위배된다.

(3) 승인절차 설정은 경직되어 있으며, 현재 승인절차는 "이사 -> 부회장 -> 회장 -> 이사회"로 되어 있으며, 필요한 경우

"이사 -> 회장 -> 이사회"로 변경합니다. 이 솔루션에서는 소스 코드 수정을 통해서만 구현 가능하며 클라이언트는 승인 프로세스를 사용자 정의할 수 없습니다.

책임 사슬 패턴

책임 사슬 패턴:

요청 송신자와 수신자를 함께 결합하는 것을 피하고, 여러 객체가 요청을 수신할 수 있도록 하고, 이러한 객체를 체인으로 연결하고, 이 체인을 따라 요청을 전달합니다.

객체가 그것을 처리할 때까지. 책임 사슬 패턴은 객체-행동 패턴입니다.

책임 사슬 패턴 구조 다이어그램

책임 패턴 역할의 사슬

핸들러(추상 프로세서):

요청을 처리하기 위한 인터페이스를 정의하며 일반적으로 추상 클래스로 설계되는데, 특정 핸들러마다 요청을 처리하는 방식이 다르기 때문에

따라서 추상 요청 처리 방법이 정의되어 있습니다. 각 핸들러의 다음 홈은 여전히 ​​핸들러이기 때문에 추상 핸들러에 정의됩니다.

다음 홈에 대한 참조로서 추상 프로세서 유형의 객체(예: 구조 다이어그램의 후속 항목)입니다. 이 참조를 통해 핸들러를 체인으로 연결할 수 있습니다.

ConcreteHandler(특정 핸들러):

사용자 요청을 처리할 수 있는 추상 핸들러의 하위 클래스로, 추상 핸들러에서 정의한 추상 요청 처리 방법을 구상 핸들러 클래스에 구현한다.

요청을 처리하기 전에 해당 처리 권한이 있는지 판단해야 하며, 요청이 처리될 수 있으면 처리되고, 그렇지 않으면 요청이 후임자에게 전달됩니다.

요청 전달을 위해 특정 핸들러에서 체인의 다음 객체에 액세스할 수 있습니다. 책임 사슬 패턴에서는 많은 객체가 각 객체에 종속됩니다.

인용은 연결되어 체인을 형성합니다. 체인의 객체가 요청을 처리하기로 결정할 때까지 요청은 체인을 통해 전달됩니다. 누가 이 요청을 했는지

클라이언트는 체인의 어떤 객체가 요청을 처리하게 되는지 알 수 없습니다. 이를 통해 시스템은 클라이언트에 영향을 주지 않고 체인을 동적으로 재구성하고 책임을 할당할 수 있습니다.

메모:

블로그:
Domineering Rogue Temperament_C#, Architecture Road, SpringBoot-CSDN 블로그

성취하다

위의 조달 승인 시스템은 책임 사슬 모델로 구현됩니다.

1. 요청 클래스로 새 구매 주문 객체를 생성합니다.

import lombok.Data;

//采购单:请求类
@Data
public 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;
    }

}

2. 추상 프로세서로 새로운 승인자 클래스를 생성합니다.

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

3. 특정 프로세서로 새 디렉터 클래스를 생성합니다.

//主任类:具体处理者
public class Director extends Approver {

    public Director(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        if(request.getAmount()<50000){
            System.out.println("主任"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
        }else{
            //转发请求
            this.successor.processRequest(request);
        }
    }
}

4. 새로운 부회장 카테고리도 생성

//副董事长类:具体处理者
public class VicePresident extends Approver {

    public VicePresident(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        if(request.getAmount()<100000){
            System.out.println("副董事长"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
        }else{
            //转发请求
            this.successor.processRequest(request);
        }
    }
}

5. 새로운 디렉터 클래스도 생성하세요

//董事长类:具体处理者
public class President extends Approver {

    public President(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        if(request.getAmount()<500000){
            System.out.println("董事长"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
        }else{
            //转发请求
            this.successor.processRequest(request);
        }
    }
}

6. 새로운 보드 클래스도 생성하세요

//董事会类:具体处理者
public class Congress extends Approver {

    public Congress(String name) {
        super(name);
    }

    //具体请求处理方法
    public void processRequest(PurchaseRequest request) {
        //处理请求
        System.out.println("董事会"+this.name+"审批采购单:"+request.getNumber()+",金额:"+request.getAmount()+",元,采购目的:"+request.getPurpose());
    }
}

7. 클라이언트 호출 방법

public class Client {
    public static void main(String[] args) {

        Approver zhuren,fudongshi,dongshi,dongshihui;
        zhuren = new Director("张主任");
        fudongshi = new VicePresident("李副董事长");
        dongshi = new President("王董事长");
        dongshihui = new Congress("董事会");

        //创建职责链
        zhuren.setSuccessor(fudongshi);
        fudongshi.setSuccessor(dongshi);
        dongshi.setSuccessor(dongshihui);

        //创建采购单
        PurchaseRequest pr1 = new PurchaseRequest(49000,1001,"购买网线");
        zhuren.processRequest(pr1);

        PurchaseRequest pr2 = new PurchaseRequest(89000,1002,"购买服务器");
        zhuren.processRequest(pr2);

        PurchaseRequest pr3 = new PurchaseRequest(190000,1003,"购买机柜");
        zhuren.processRequest(pr3);

        PurchaseRequest pr4 = new PurchaseRequest(600000,1004,"购买机房");
        zhuren.processRequest(pr4);

    }
}

8. 요약

50,000~80,000위안(80,000위안 제외)의 구매 주문을 승인하기 위해 관리자(Manager) 역할을 추가하는 등 시스템에 새로운 특정 프로세서를 추가해야 하는 경우,

Approver 클래스에 정의된 추상 처리 메서드를 구현하려면 추상 핸들러 클래스 Approver의 하위 클래스로 새로운 특정 핸들러 클래스 Manager를 작성해야 합니다.

구매 금액이 80,000위안 이상인 경우 다음 구매자에게 요청이 전달됩니다.

책임 사슬 패턴의 주요 장점:

(1) 책임 체인 패턴을 사용하면 객체는 어떤 다른 객체가 자신의 요청을 처리하는지 알 필요가 없으며 객체는 요청이 처리된다는 것만 알면 됩니다.

수신자도 발신자도 상대방에 대한 명확한 정보를 갖고 있지 않으며, 체인에 있는 객체는 체인의 구조를 알 필요가 없으며, 체인 생성에 대한 책임은 클라이언트에게 있습니다.

시스템의 결합 정도가 감소합니다.

(2) 요청 처리 객체는 후속 객체에 대한 참조만 유지하면 되며 모든 후보 핸들러에 대한 참조를 유지할 필요는 없습니다.

이는 객체의 상호 연결을 단순화합니다.

(3) 객체에 책임을 할당할 때 책임 체인은 런타임에 체인을 동적으로 증가시켜 더 많은 유연성을 제공할 수 있습니다.

또는 요청 처리 책임을 추가하거나 변경하도록 수정합니다.

(4) 새로운 특정 요청 핸들러를 시스템에 추가할 때 원래 시스템의 코드를 수정할 필요가 없으며 클라이언트 측에서 링크를 다시 설정하기만 하면 됩니다.

이러한 관점에서 볼 때 이는 "개방 및 폐쇄 원칙"에 부합합니다.

책임 사슬 패턴의 주요 단점은 다음과 같습니다.

(1) 요청에는 명확한 수신자가 없기 때문에 처리된다는 보장이 없으며 요청은 체인이 끝날 때까지 처리되지 않을 수 있습니다.

책임 체인이 제대로 구성되지 않아 요청이 처리되지 않을 수도 있습니다.

(2) 상대적으로 긴 책임 체인의 경우 요청 처리에 여러 처리 개체가 포함될 수 있으며 이는 시스템 성능에 어느 정도 영향을 미치며 코드 디버깅에 편리하지 않습니다.

(3) 링크가 제대로 설정되지 않으면 순환 호출이 발생하여 시스템이 무한 루프에 빠질 수 있습니다.

적용 가능한 장면:

(1) 동일한 요청을 처리할 수 있는 개체가 여러 개 있는데, 어떤 개체가 요청을 처리하는지는 런타임에 결정되며, 클라이언트는 해당 요청을 체인에 제출하기만 하면 됩니다.

요청이 누구를 위해 처리되고 어떻게 처리되는지 신경 쓸 필요가 없습니다.

(2) 수신자를 명시적으로 지정하지 않고 여러 개체 중 하나에 요청을 제출합니다.

(3) 요청을 처리하기 위해 객체 그룹을 동적으로 지정할 수 있으며, 클라이언트는 요청을 처리하기 위해 책임 체인을 동적으로 생성할 수 있으며 체인의 프로세서 순서를 변경할 수도 있습니다.

추천

출처blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/131917298