[Design Patterns - Study Notes] 23 Design Patterns - Chain of Responsibility (Chain of Responsibility) (principle explanation + application scenario introduction + case introduction + Java code implementation)

Case introduction

Procurement approval items of the school OA system: The requirements are

  • Buyer purchases teaching equipment
  • If the amount is less than or equal to 5000 (0<x<=5000), it will be approved by the teaching director
  • If the amount is less than or equal to 10,000 (5,000<x<=10,000), it will be approved by the dean
  • If the amount is less than or equal to 30,000 (10,000<x<=30,000), it will be approved by the vice president
  • If the amount exceeds 30,000 (30,000<x), it shall be approved by the principal

Traditional way to achieve

Create a class corresponding to different approvers, write an if else judgment program in the client, and let different approvers handle it if different conditions are met.

analyze

The client will use branch judgment (such as switch) to process different purchase requests, so there are the following problems

  • If the amount approved by personnel at each level changes, the client code also needs to change.
  • The client must clearly know how many approval levels there are and how to access them. In this way, there is a strong coupling relationship between the class that processes a purchase request and the Approver, which is not conducive to code expansion and maintenance.

【Improve】

Use chain of responsibility pattern

introduce

Basic introduction

  • The chain of responsibility pattern, also called the chain of responsibility pattern, creates a chain of receiver objects for requests (as shown in the simple diagram below). This pattern decouples the sender and receiver of the request. When the sender sends a request, the receiver will find out who should be responsible for processing the request one by one according to the order of the chain of responsibilities.

Insert image description here

  • Chain of Responsibility pattern usually each receiver contains a reference to another receiver. If an object cannot handle the request, then it will pass the same request to the next recipient, and so on. If no one can handle it, it will finally prompt or report an error saying that it cannot be processed.
  • This type of design pattern is a behavioral pattern

Characters

Insert image description here

  • Handler(抽象处理者): Defines an interface for processing requests, and also has variables to save other Handlers
  • ConcreteHandler(具体处理者): To process the request it is responsible for, it can access its successor (i.e. the next processor). If the current request can be processed, it will be processed. Otherwise, the request will be handed over to the successor for processing, thus forming a chain of responsibilities.
  • Request(请求对象): Contains many attributes, representing a request
  • Client(请求者):send request

Application scenarios

  • When there are multiple objects that can handle the same request, such as: multi-level requests, approval processes such as leave/salary increase, Tomcat's processing of Encoding in Java Web, interceptors

Case implementation

Case number one

Class Diagram

Insert image description here

accomplish

【Purchase Request】

package com.atguigu.responsibilitychain;

/**
 * 请求类
 */
public class PurchaseRequest {
    
    

   /**
    * 请求类型
    */
   private int type = 0;
   /**
    * 请求金额
    */
   private float price = 0.0f;
   private int id = 0;

   /**
    * 构造器
    * @param type
    * @param price
    * @param id
    */
   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;
   }

}

[Abstract request handler]

package com.atguigu.responsibilitychain;

/**
 * 请求处理者
 */
public abstract class Approver {
    
    

   /**
    * 下一个处理者
    */
   Approver approver;
   /**
    * 当前处理者名字
    */
   String name;

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

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

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

}

【Department level handler】

package com.atguigu.responsibilitychain;

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);
      }
   }

}

【College Level Processor】

package com.atguigu.responsibilitychain;

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);
      }
   }
}

【Vice-Chancellor】

package com.atguigu.responsibilitychain;

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);
      }
   }
}

【headmaster】

package com.atguigu.responsibilitychain;

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);
      }
   }
}

【Client】

package com.atguigu.responsibilitychain;

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);
   }

}

【Main category】

 请求编号 id= 1 被 佟校长 处理
 请求编号 id= 1 被 佟校长 处理

Process finished with exit code 0

Case 2

Class Diagram

Insert image description here

accomplish

【Question type】

package com.atguigu.responsibilitychain.Sample;

public class Trouble {
    
    
    /**
     * 问题编号
     */
    private int number;

    /**
     * 根据编号生成问题
     * @param number
     */
    public Trouble(int number) {
    
        
        this.number = number;
    }

    /**
     * 获取问题编号
     * @return
     */
    public int getNumber() {
    
            
        return number;
    }

    /**
     * 代表问题的字符串
     * @return
     */
    public String toString() {
    
          
        return "[Trouble " + number + "]";
    }
}

[Abstract class used to solve problems]

package com.atguigu.responsibilitychain.Sample;

public abstract class Support {
    
    
    /**
     * 解决问题的实例的名字
     */
    private String name;
    /**
     * 要推卸给的对象
     */
    private Support next;

    /**
     * 生成解决问题的实例
     *
     * @param name
     */
    public Support(String name) {
    
    
        this.name = name;
    }

    /**
     * 设置要推卸给的对象
     *
     * @param next
     * @return
     */
    public Support setNext(Support next) {
    
    
        this.next = next;
        return next;
    }

    /**
     * 解决问题的步骤
     * 调用了抽象方法resolve,这里属于模板方法模式
     * @param trouble
     */
    public void support(Trouble trouble) {
    
    
        if (resolve(trouble)) {
    
    
            done(trouble);
        } else if (next != null) {
    
    
            // 问题还没有解决,让下一个对象来尝试解决
            next.support(trouble);
        } else {
    
    
            // 所有对象都没有办法解决该问题,提示用户 问题没办法解决
            fail(trouble);
        }
    }

    /**
     * 显示字符串
     *
     * @return
     */
    public String toString() {
    
    
        return "[" + name + "]";
    }

    /**
     * 解决问题的方法
     * 需要子类去具体实现,如果解决了问题,就返回true
     *
     * @param trouble
     * @return
     */
    protected abstract boolean resolve(Trouble trouble);

    /**
     * 解决
     *
     * @param trouble
     */
    protected void done(Trouble trouble) {
    
    
        System.out.println(trouble + " is resolved by " + this + ".");
    }

    /**
     * 未解决
     *
     * @param trouble
     */
    protected void fail(Trouble trouble) {
    
    
        System.out.println(trouble + " cannot be resolved.");
    }
}

[Category that does not solve the problem]

package com.atguigu.responsibilitychain.Sample;

public class NoSupport extends Support {
    
    
    public NoSupport(String name) {
    
    
        super(name);
    }

    /**
     * 解决问题的方法 
     * 什么都解决不了,直接返回false
     * @param trouble
     * @return
     */
    protected boolean resolve(Trouble trouble) {
    
         
        return false; 
    }
}

[Specific problem solving category: As long as the problem number is less than limit, it can be solved]

package com.atguigu.responsibilitychain.Sample;

/**
 * 可以解决编号小于limit的问题
 */
public class LimitSupport extends Support {
    
    
    private int limit;

    /**
     * 构造函数
     *
     * @param name
     * @param limit
     */
    public LimitSupport(String name, int limit) {
    
    
        super(name);
        this.limit = limit;
    }

    protected boolean resolve(Trouble trouble) {
    
    
        if (trouble.getNumber() < limit) {
    
    
            /*
            解决了什么什么问题
             */
            return true;
        } else {
    
    
            return false;
        }
    }
} 

[Specific type of problem to solve: As long as the problem number is an odd number, it can be solved]

package com.atguigu.responsibilitychain.Sample;

public class OddSupport extends Support {
    
    
    /**
     * 构造函数
     * @param name
     */
    public OddSupport(String name) {
    
                    
        super(name);
    }
    protected boolean resolve(Trouble trouble) {
    
        
        if (trouble.getNumber() % 2 == 1) {
    
    
            return true;
        } else {
    
    
            return false;
        }
    }
}

[Specific problem-solving class: Only problems with specified numbers can be solved]

package com.atguigu.responsibilitychain.Sample;

public class SpecialSupport extends Support {
    
    
    private int number;

    /**
     * 构造函数
     *
     * @param name
     * @param number
     */
    public SpecialSupport(String name, int number) {
    
    
        super(name);
        this.number = number;
    }

    /**
     * 解决问题的方法
     *
     * @param trouble
     * @return
     */
    protected boolean resolve(Trouble trouble) {
    
    
        if (trouble.getNumber() == number) {
    
    
            return true;
        } else {
    
    
            return false;
        }
    }
}

【Main category】

package com.atguigu.responsibilitychain.Sample;

public class Main {
    
    
    public static void main(String[] args) {
    
    
        // 生成六个解决问题的实例
        Support alice = new NoSupport("Alice");
        Support bob = new LimitSupport("Bob", 100);
        Support charlie = new SpecialSupport("Charlie", 429);
        Support diana = new LimitSupport("Diana", 200);
        Support elmo = new OddSupport("Elmo");
        Support fred = new LimitSupport("Fred", 300);
        // 连接对象,形成职责链
        alice.setNext(bob).setNext(charlie).setNext(diana).setNext(elmo).setNext(fred);
        // 制造各种问题,增长步长大一点,让问题的编号更加的分散
        for (int i = 0; i < 500; i += 33) {
    
    
            alice.support(new Trouble(i));
        }
    }
}

【run】

[Trouble 0] is resolved by [Bob].
[Trouble 33] is resolved by [Bob].
[Trouble 66] is resolved by [Bob].
[Trouble 99] is resolved by [Bob].
[Trouble 132] is resolved by [Diana].
[Trouble 165] is resolved by [Diana].
[Trouble 198] is resolved by [Diana].
[Trouble 231] is resolved by [Elmo].
[Trouble 264] is resolved by [Fred].
[Trouble 297] is resolved by [Elmo].
[Trouble 330] cannot be resolved.
[Trouble 363] is resolved by [Elmo].
[Trouble 396] cannot be resolved.
[Trouble 429] is resolved by [Charlie].
[Trouble 462] cannot be resolved.
[Trouble 495] is resolved by [Elmo].

Process finished with exit code 0

Application of chain of responsibility pattern in Spring

Insert image description here

  • In the flow chart of the springmvc request, the interceptor related methods interceptor.preHandler and so on are executed. When processing the SpringMvc request, the chain of responsibility mode and the adapter mode are used.
  • HandlerExecutionChain is mainly responsible for the execution of the request interceptor and request processing, but it does not process the request itself. It just assigns the request to the registered processor on the chain for execution. This is the implementation method of the chain of responsibility, which reduces the gap between the chain of responsibility itself and the processing logic. Coupling standardizes the processing flow
  • HandlerExecutionChain maintains a collection of Handlerlnterceptor, to which the corresponding interceptor can be registered

Insert image description here

Summarize

【advantage】

  • Separate requests and processing to achieve decoupling and improve system flexibility
  • Simplified objects so that they do not need to know the structure of the chain
  • The structure of the chain can be dynamically adjusted
  • Let everyone ConcreteHandlerfocus more on their own work, and the logic of the program will be clearer

【shortcoming】

  • Performance will suffer, especially if the chain is relatively long. Therefore, it is necessary to control the maximum number of nodes in the chain. Generally, a maximum number of nodes is set in the Handler. In the setNext() method, it is judged whether the threshold has been exceeded and the chain is not allowed to be established to avoid overly long chains that damage system performance.
  • Debugging is inconvenient. It uses a recursive method. The logic may be complicated during debugging.

【Q&A】

In windowing systems, the chain of responsibility pattern is often used. In the window of the window system, there are buttons, text input boxes, check boxes and other components (also called components or controls). When the mouse is clicked, how is the processing of the mouse click event propagated? Which component is next (the object to be transferred to) in the chain of responsibility pattern?

Answer: Generally, what is saved in the next field is the parent window of the control.

As shown in the small dialog box below. When the focus moves to the "Font" list box, press the ↑↓ keys on the keyboard to select the corresponding font. However, if you press the ↑ key when the focus is moved to the "Show Balanced Font" check box, the focus will move to the "Font" list box. After that, even if the ↓ key is pressed, the focus will not return to the check box. . Please use the Chain of Responsibility model thinking method to explain this problem.

Insert image description here

If the focus is on the list box, the list box will handle the event when the ↑↓ key is pressed by itself, and will not offload the request to next. However, if the focus is on the check box, it will not handle the event when the ↑↓ key is pressed. event, but offloads the request to the parent dialog box corresponding to next. When the parent dialog box receives the event that the ↑↓ key is pressed, it will move the focus to the list boxInsert image description here

Article description

  • This article is my study notes for studying Shang Silicon Valley. Most of the content in the article comes from Shang Silicon Valley videos ( click to learn Shang Silicon Valley related courses ), and some of the content comes from my own thinking. I publish this article to help other people who study more conveniently. Organize your own notes or learn relevant knowledge directly through articles. If there is any infringement, please contact us to delete it. Finally, I would like to express my gratitude to Shang Silicon Valley for its high-quality courses.
  • I also simultaneously read the book "Graphic Design Pattern" (Graphic Design Pattern/(Japanese) Hiroshi Yuki; translated by Yang Wenxuan - Beijing: People's Posts and Telecommunications Press, 2017.1), and then integrated the contents of the two to make the knowledge points more comprehensive.

Guess you like

Origin blog.csdn.net/laodanqiu/article/details/132300599