【デザインパターン-11】責任連鎖パターン

[1] 責任連鎖モデル

名前が示すように、Chain of Responsibility パターンは、リクエストの受信者オブジェクトのチェーンを作成します。このパターンは、リクエストのタイプに応じて、リクエストの送信者と受信者を切り離します。このタイプの設計パターンは動作パターンです。

このパターンでは、通常、各レシーバーに別のレシーバーへの参照が含まれます。オブジェクトがリクエストを処理できない場合は、同じリクエストを次の受信者に渡します。

【2】はじめに

(1) 趣旨

リクエストの送信側と受信側を結合することを避け、複数のオブジェクトがリクエストを受信できるようにし、これらのオブジェクトをチェーンに接続し、オブジェクトが処理するまでこのチェーンに沿ってリクエストを渡します。

(2) 主な解決策

責任チェーン上のプロセッサはリクエストの処理を担当します。クライアントはリクエストを責任チェーンに送信するだけでよく、リクエストの処理の詳細や配信について気にする必要はありません。したがって、責任チェーンは送信者を分離します。リクエストのプロセッサからのリクエストの。

(3) 使用する場合

メッセージを処理するときにフィルタリングする方法は数多くあります。

(4) 解決方法

インターセプトされたクラスはすべて、統一インターフェイスを実装します。

(5) キーコード

Handler内で自身を集約し、HandlerRequest内で適切かどうかを判断し、条件を満たさない場合は渡し、誰に渡す前に設定します。

(6) 応用例

1.紅楼夢の「太鼓をたたいて花を渡す」。2. JS でのイベントのバブリング。3. JAVA WEBのApache Tomcatによるエンコード処理、Struts2のインターセプタ、jspサーブレットのフィルタ。

(7) メリット

1. カップリングを減らします。これにより、リクエストの送信者と受信者が分離されます。2. オブジェクトが簡略化されます。そのため、オブジェクトはチェーンの構造を知る必要がありません。3. オブジェクトに対する責任の割り当ての柔軟性を強化します。チェーン内のメンバーを変更したり、その順序を変更したりすることで、責任を動的に追加または削除することができます。4. 新しいリクエスト処理クラスを追加すると非常に便利です。

(8) デメリット

1. リクエストが受け入れられるという保証はありません。2. システムのパフォーマンスにある程度の影響があり、コードのデバッグが不便になり、循環呼び出しが発生する可能性があります。3. ランタイムの特性を観察するのは容易ではなく、デバッグの妨げとなる場合があります。

(9) 利用シーン

1. 同じリクエストを処理できるオブジェクトが複数あり、リクエストを処理する特定のオブジェクトは実行時に自動的に決定されます。2. 受信者を明示的に指定せずに、複数のオブジェクトの 1 つにリクエストを送信します。3. リクエストを処理するためにオブジェクトのグループを動的に指定できます。

(10) 注意事項

JAVA WEB には多くのアプリケーションがあります。

【3】実現事例

(1 はじめに

詳細ログレベルを持つ抽象クラス AbstractLogger を作成します。次に、3 種類のロガーを作成します。これらはすべて AbstractLogger を拡張しています。各ロガー メッセージのレベルが独自のレベルに属しているかどうか。属している場合は、それに応じて出力されます。そうでない場合は出力されず、メッセージは次のロガーに渡されます。
ここに画像の説明を挿入

(2) 抽象ロガークラスの作成


public abstract class AbstractLogger {
    
    
   public static int INFO = 1;
   public static int DEBUG = 2;
   public static int ERROR = 3;
 
   protected int level;
 
   //责任链中的下一个元素
   protected AbstractLogger nextLogger;
 
   public void setNextLogger(AbstractLogger nextLogger){
    
    
      this.nextLogger = nextLogger;
   }
 
   public void logMessage(int level, String message){
    
    
      // 如果传的参数level大于等于已有的level,则写日志
      if(this.level <= level){
    
    
         write(message);
      }
      // 如果下一个记录器不为null
      if(nextLogger !=null){
    
    
         nextLogger.logMessage(level, message);
      }
   }
 
   abstract protected void write(String message);
   
}

(3) ロガークラスを拡張したエンティティクラスを作成する

public class ConsoleLogger extends AbstractLogger {
    
    
 
   public ConsoleLogger(int level){
    
    
      this.level = level;
   }
 
   @Override
   protected void write(String message) {
    
        
      System.out.println("Standard Console::Logger: " + message);
   }
}


public class ErrorLogger extends AbstractLogger {
    
    
 
   public ErrorLogger(int level){
    
    
      this.level = level;
   }
 
   @Override
   protected void write(String message) {
    
        
      System.out.println("Error Console::Logger: " + message);
   }
}


public class FileLogger extends AbstractLogger {
    
    
 
   public FileLogger(int level){
    
    
      this.level = level;
   }
 
   @Override
   protected void write(String message) {
    
        
      System.out.println("File::Logger: " + message);
   }
}

(4) さまざまな種類のロガーを作成する

さまざまなタイプのロガーを作成します。異なるエラー レベルを与え、各ロガーで次のロガーを設定します。各ロガー内の次のロガーはチェーンの一部を表します。


public class ChainPatternDemo {
    
    
   
   private static AbstractLogger getChainOfLoggers(){
    
    
 
      AbstractLogger errorLogger = new ErrorLogger(AbstractLogger.ERROR);
      AbstractLogger fileLogger = new FileLogger(AbstractLogger.DEBUG);
      AbstractLogger consoleLogger = new ConsoleLogger(AbstractLogger.INFO);
 
      errorLogger.setNextLogger(fileLogger);
      fileLogger.setNextLogger(consoleLogger);
 
      return errorLogger;  
   }
 
   public static void main(String[] args) {
    
    
      AbstractLogger loggerChain = getChainOfLoggers();
 
      loggerChain.logMessage(AbstractLogger.INFO, "This is an information.");
 
      loggerChain.logMessage(AbstractLogger.DEBUG, 
         "This is a debug level information.");
 
      loggerChain.logMessage(AbstractLogger.ERROR, 
         "This is an error information.");
   }
}

出力結果

Standard Console::Logger: This is an information.
File::Logger: This is a debug level information.
Standard Console::Logger: This is a debug level information.
Error Console::Logger: This is an error information.
File::Logger: This is an error information.
Standard Console::Logger: This is an error information.

【4】その他の事例紹介

(1) 責任連鎖モデルにより調達承認の問題を解決

1- 購入リクエストのエンティティ クラスを作成します。

/**
 * @ClassName: PurchaseRequest
 * @Author: AllenSun
 * @Date: 2020/3/9 22:36
 */
public class PurchaseRequest {
    
    

    private int type=0;
    private float price=0.0f;
    private int id=0;

    public PurchaseRequest(int type, float price, int id) {
    
    
        this.type = type;
        this.price = price;
        this.id = id;
    }

    public int getType() {
    
    
        return type;
    }

    public float getPrice() {
    
    
        return price;
    }

    public int getId() {
    
    
        return id;
    }
}

2-抽象購入承認クラスを作成する

/**
 * @ClassName: Approver
 * @Author: AllenSun
 * @Date: 2020/3/9 22:31
 */
public abstract class Approver {
    
    
    //下一个处理者
    Approver approver;
    //名字
    String name;

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

    //下一个处理者
    public void setApprover(Approver approver) {
    
    
        this.approver = approver;
    }

    //处理审批请求的方法,得到一个请求,处理是子类完成,因此该方法做成抽象
    public abstract void processRequest(PurchaseRequest purchaseRequest);
}

3- 承認クラスを拡張するエンティティ クラスを作成する

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
public class DepartmentApprover extends Approver {
    
    
    public DepartmentApprover(String name) {
    
    
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
    
    
        if(purchaseRequest.getPrice()<=5000){
    
    
            System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");

        } else {
    
    
            approver.processRequest(purchaseRequest);
        }
    }
}

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
public class CollegeApprover extends Approver {
    
    
    public CollegeApprover(String name) {
    
    
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
    
    
        if(purchaseRequest.getPrice()>5000&&purchaseRequest.getPrice()<=10000){
    
    
            System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");

        } else {
    
    
            approver.processRequest(purchaseRequest);
        }

    }
}

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
public class ViceSchoolMasterApprover extends Approver {
    
    
    public ViceSchoolMasterApprover(String name) {
    
    
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
    
    
        if(purchaseRequest.getPrice()>10000&&purchaseRequest.getPrice()<=30000){
    
    
            System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");

        } else {
    
    
            approver.processRequest(purchaseRequest);
        }
    }
}

/**
 * @ClassName: CollegeApprover
 * @Author: AllenSun
 * @Date: 2020/3/9 22:34
 */
public class SchoolMasterApprover extends Approver {
    
    
    public SchoolMasterApprover(String name) {
    
    
        super(name);
    }

    @Override
    public void processRequest(PurchaseRequest purchaseRequest) {
    
    
        if(purchaseRequest.getPrice()>30000){
    
    
            System.out.println("请求编号id="+purchaseRequest.getId()+"被"+this.name+"处理");

        } else {
    
    
            approver.processRequest(purchaseRequest);
        }
    }
}

4- 責任の連鎖を設定し、クラスをテストする

/**
 * @ClassName: Client
 * @Author: AllenSun
 * @Date: 2020/3/9 22:50
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        //创建一个请求
        PurchaseRequest purchaseRequest=new PurchaseRequest(1,31000,1);

        //创建相关的审批人
        DepartmentApprover departmentApprover=new DepartmentApprover("张主任");
        CollegeApprover collegeApprover=new CollegeApprover("李院长");
        ViceSchoolMasterApprover viceSchoolMasterApprover=new ViceSchoolMasterApprover("王副校长");
        SchoolMasterApprover schoolMasterApprover=new SchoolMasterApprover("孙校长");

        //需要把各个审批级别的下一个设置好(处理人构成环形)
        departmentApprover.setApprover(collegeApprover);
        collegeApprover.setApprover(viceSchoolMasterApprover);
        viceSchoolMasterApprover.setApprover(schoolMasterApprover);
        schoolMasterApprover.setApprover(departmentApprover);

        departmentApprover.processRequest(purchaseRequest);
        viceSchoolMasterApprover.processRequest(purchaseRequest);
    }
}

(2) 責任連鎖モードによりログイン時のデータ検証の問題を解決

責任連鎖モデルは日常生活の中で比較的一般的であり、私たちは通常、仕事をしたり、いくつかの事柄に対処したりしますが、多くの場合、それはさまざまな部門が協力して完了するタスクです。そして、各部門にはそれぞれ責任があるため、プロジェクトが中途半端に終わると次の部門に引き継がれ、すべての部門が通過するまでプロジェクトが完了しないことがよくあります。

1- エンティティ クラスのメンバーを作成する

@Data
public class Member {
    
    
    private String loginName;
    private String loginPass;
    private String roleName;

    public Member(String loginName, String loginPass) {
    
    
        this.loginName = loginName;
        this.loginPass = loginPass;
    }
}

2-従来の方法でのログイン確認コード

public class MemberService {
    
    
    public static void main(String[] args) {
    
    
        MemberService service = new MemberService();
        service.login("tom", "666");
    }

    public void login(String loginName, String loginPass) {
    
    
        if (StringUtils.isEmpty(loginName) ||
                StringUtils.isEmpty(loginPass)) {
    
    
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");
        Member member = checkExists(loginName, loginPass);
        if (null == member) {
    
    
            System.out.println("用户不存在");
            return;
        }
        System.out.println("登录成功!");
        if (!"管理员".equals(member.getRoleName())) {
    
    
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");
    }

    private Member checkExists(String loginName, String loginPass) {
    
    
        Member member = new Member(loginName, loginPass);
        member.setRoleName("管理员");
        return member;
    }
}

3- 責任連鎖モードの最適化の使用を開始し、抽象処理クラス ハンドラーを作成します。

public abstract class Handler {
    
    
    protected Handler next;
    public void next(Handler next){
    
     this.next = next;}
    public abstract void doHandler(Member member);
}

4- 処理クラスを拡張するエンティティ クラスを作成する

null 以外の検証 ValidateHandler クラス、ログイン検証 LoginHandler クラス、権限検証 AuthHandler クラスをそれぞれ作成します。

public class ValidateHandler extends Handler {
    
    
    public void doHandler(Member member) {
    
    
        if (StringUtils.isEmpty(member.getLoginName()) ||
                StringUtils.isEmpty(member.getLoginPass())) {
    
    
            System.out.println("用户名和密码为空");
            return;
        }
        System.out.println("用户名和密码不为空,可以往下执行");
        next.doHandler(member);
    }
}

public class LoginHandler extends Handler {
    
    
    public void doHandler(Member member) {
    
    
        System.out.println("登录成功!");
        member.setRoleName("管理员");
        next.doHandler(member);
    }
}

public class AuthHandler extends Handler {
    
    
    public void doHandler(Member member) {
    
    
        if (!"管理员".equals(member.getRoleName())) {
    
    
            System.out.println("您不是管理员,没有操作权限");
            return;
        }
        System.out.println("允许操作");
    }
}

5- サービス層でチェーンをセットアップする

public class MemberService {
    
    

    public void login(String loginName, String loginPass) {
    
    
        Handler validateHandler = new ValidateHandler();
        Handler loginHandler = new LoginHandler();
        Handler authHandler = new AuthHandler();
        validateHandler.next(loginHandler);
        loginHandler.next(authHandler);
        validateHandler.doHandler(new Member(loginName, loginPass));
    }
}

6- テストコード

public class Test {
    
    
    public static void main(String[] args) {
    
    
        MemberService memberService = new MemberService();
        memberService.login("tom","666");
    }
}

試験結果

用户名和密码校验成功,可以往下执行
登录成功!
您是管理员,允许操作

7- 責任チェーンモード + ビルダーモード併用の最適化

責任連鎖モデルはチェーン構造になっており、上記のコードではチェーン構造を組み立てる役割を担っているのが MemberService であることがわかりますが、チェーン
構造が長い場合、MemberService の作業は非常に煩雑になります。 MemberService のコードは比較的肥大化しているため、プロセッサまたはメッセージ タイプに対する後続の変更は MemberService 内で変更する必要があり、これはオープンとクローズの原則に準拠していません。これらの問題の原因は、チェーン構造の組み立てが複雑すぎるためです. 複雑な構造を作成するには、当然ビルダー モードが考えられます. ビルダー モードを使用すると、MemberService で指定された処理ノード オブジェクトを自動的にチェーンすることができます. 、顧客は処理ノード オブジェクトを指定するだけで済み、他は何も気にする必要はありません。顧客が指定した処理ノード オブジェクトの順序は異なり、構築されるチェーン構造も異なります。これを変更して、Handler のコードを変更しましょう。

public abstract class Handler<T> {
    
    
    protected Handler next;
    //public void next(Handler next){ this.next = next;}
    private void next(Handler next){
    
     this.next = next;}

    public abstract void doHandler(Member member);

    public static class Builder<T>{
    
    
        private Handler<T> head;
        private Handler<T> tail;

        public Builder<T> addHandler(Handler handler){
    
    
           // do {
    
    
                if (this.head == null) {
    
    
                    this.head = this.tail = handler;
                    return this;
                }
                this.tail.next(handler);
                this.tail = handler;
           // }while (false);//真正框架中,如果是双向链表,会判断是否已经到了尾部
            return this;
        }

        public Handler<T> build(){
    
    
            return this.head;
        }
    }
}

次に、MemberService のコードを変更します。

public class MemberService {
    
    
    public void login(String loginName,String loginPass){
    
    
        Handler.Builder builder = new Handler.Builder();
        builder.addHandler(new ValidateHandler())
               .addHandler(new LoginHandler())
               .addHandler(new AuthHandler());
        builder.build().doHandler(new Member(loginName,loginPass));
        //用过Netty的人,肯定见过
    }
}

おすすめ

転載: blog.csdn.net/weixin_44823875/article/details/128990803