Java design pattern - Chain of Responsibility Pattern

1, Introduction

The 23 design modes are roughly divided into three categories:

There are 5 kinds of creation modes: factory method mode, abstract factory mode, singleton mode, prototype mode and builder mode.

7 types (structural mode): adapter mode, decorator mode, agent mode, appearance mode, bridge mode, combination mode and sharing mode.

11 kinds of behavioral modes: policy mode, template method mode, observer mode, iterator mode, responsibility chain mode, command mode, memo mode, status mode, visitor mode, mediator mode and interpreter mode.

Behavior type can be divided by the relationship between classes:

Basic introduction of responsibility chain mode:

  • The Chain of Responsibility Pattern, also known as the responsibility chain pattern, creates a chain of receiver objects for requests (a simple diagram is shown below). This mode decouples the sender and receiver of the request
  • Typically, each recipient of the responsibility chain pattern contains a reference to another recipient. If an object cannot process the request, it passes the same request to the next recipient, and so on. This type of design pattern belongs to behavioral pattern
  • The responsibility chain pattern gives multiple objects the opportunity to process the request, thus avoiding the coupling between the sender and receiver of the request. Connect these objects into a chain and pass the request along the chain until an object processes it

2, Responsibility chain model

1. Schematic diagram of responsibility chain mode

Description of schematic class diagram:

  • Handler: an abstract handler that defines an interface for processing requests and contains another handler object
  • ConcreteHandler A / B: a specific handler that handles the request for which it is responsible, and can access its successor (i.e. the next handler). If you can handle the current request, you can handle it yourself; Otherwise, the request is handed over to the successor to process, forming a chain of responsibilities
  • Request: contains many attributes, representing a request.

3, Specific needs

1.OA system procurement approval requirements

For the procurement approval project of the school OA system, the requirements are: the purchaser purchases teaching equipment

  • If the amount is less than or equal to 5000, it shall be approved by the teaching Director (0 < = x < = 5000)
  • If the amount is less than or equal to 10000, it shall be approved by the president (5000 < x < = 10000)
  • If the amount is less than or equal to 30000, it shall be approved by the vice president (10000 < x < = 30000)
  • If the amount exceeds 30000, it shall be approved by the president (30000 < x)

2. Traditional programmes

Idea: the traditional method is to call the corresponding Approver to complete the approval according to the purchase amount after receiving a purchase request. You can use if/else to judge

Traditional problem analysis:

  • The client will use branch judgment (such as switch) to process different purchase requests, so there are the following problems:
    • If the approved amount of personnel at all levels changes, it also needs to change at the client
    • The client must clearly know how many approval levels and access levels there are
  • In this way, there is a strong coupling relationship between the processing of a purchase request and the Approver, which is not conducive to code expansion and maintenance

3. Responsibility chain mode scheme

Thought analysis - class diagram

Concrete implementation

// PurchaseRequest.java
public class PurchaseRequest {
	private int type = 0;//Request type
	private float price = 0.0f;//Requested amount
	private int id = 0;
	
	public PurchaseRequest(int type, float price, int id) {
		super();
		this.type = type;
		this.price = price;
		this.id = id;
	}
	// get/set method omitted
}

// Approver.java
public abstract class Approver {
	Approver approver;//Next processor
	String name;//Processor name
	public Approver(String name) {
		this.name = name;
	}
	public void setApprover(Approver approver) {
		this.approver = approver;
	}
	
	//The method for processing the approval request (you need to get a request): it is completed by a specific subclass, so the method is made abstract
	public abstract void processRequest(PurchaseRequest purchaseRequest);
}

// DepartmentApprover.java / CollegeApprover.java / ...
public class DepartmentApprover extends Approver{
	public DepartmentApprover(String name) {
		super(name);
	}

	@Override
	public void processRequest(PurchaseRequest purchaseRequest) {
		if(purchaseRequest.getPrice() <= 5000) {
			System.out.println("Request number id= "+purchaseRequest.getId()+"cover"+this.name+"handle");
		}else {
			approver.processRequest(purchaseRequest);//Let the successor handle it
		}
	}
}

// Client.java
public class Client {
	public static void main(String[] args) {
		//Create a request
		PurchaseRequest purchaseRequest = new PurchaseRequest(1, 31000, 1);
		//Create relevant approvers
		DepartmentApprover departmentApprover = new DepartmentApprover("Director Zhang");
		CollegeApprover collegeApprover = new CollegeApprover("President Li");
		ViceSchoolMasterApprover viceSchoolMasterApprover = new ViceSchoolMasterApprover("Vice president Wang");
		SchoolMasterApprover schoolMasterApprover = new SchoolMasterApprover("Headmaster Zhao");
		
		//Set the approval chain (the handler should form a ring -- ensure that someone can handle it): otherwise, a null pointer exception will be reported
		//If it is not a ring, all processing must start from the first level to find the processor
		departmentApprover.setApprover(collegeApprover);
		collegeApprover.setApprover(viceSchoolMasterApprover);
		viceSchoolMasterApprover.setApprover(schoolMasterApprover);
		schoolMasterApprover.setApprover(departmentApprover);
		
		departmentApprover.processRequest(purchaseRequest);
	}
}

4, Precautions and details

  • The request and processing are separated to realize decoupling and improve the flexibility of the system
  • The object is simplified so that the object does not need to know the structure of the chain
  • The performance will be affected to a certain extent, especially when the chain is relatively long. Therefore, it is necessary to control the maximum number of nodes in the chain. Generally, set a maximum number of nodes in the Handler and judge whether the threshold has been exceeded in the setNext() method. If the threshold is exceeded, the chain is not allowed to be established to avoid the unintentional damage to the system performance caused by the super long chain
  • Because it is chained, it is inconvenient to debug. A recursive method is adopted, and the logic may be complex during debugging
  • Best application scenario: there are multiple objects that can handle the same request, which are often used, such as multi-level requests, approval processes such as leave / salary increase, Tomcat's Encoding processing in Java Web, and interceptors.

Tags: Java Design Pattern

Posted on Thu, 09 Sep 2021 17:26:34 -0400 by tcarnes