[Design Pattern-11] Chain of Responsibility Pattern

[1] Chain of Responsibility Model

As the name suggests, the Chain of Responsibility Pattern creates a chain of receiver objects for a request. This pattern decouples the sender and receiver of the request given the type of the request. This type of design pattern is a behavioral pattern.

In this pattern, typically each receiver contains a reference to another receiver. If an object cannot handle the request, it passes the same request to the next recipient, and so on.

【2】Introduction

(1) Intent

Avoid coupling the request sender and receiver together, make it possible for multiple objects to receive the request, connect these objects into a chain, and pass the request along this chain until an object handles it.

(2) The main solution

The processor on the responsibility chain is responsible for processing the request. The client only needs to send the request to the responsibility chain, and does not need to care about the processing details and delivery of the request. Therefore, the responsibility chain decouples the sender of the request from the processor of the request. .

(3) When to use

There are many ways to filter when processing messages.

(4) How to solve

The intercepted classes all implement the unified interface.

(5) Key code

Aggregate itself in the Handler, judge whether it is suitable in the HandlerRequest, and pass it down if the condition is not met, and set it before passing it to whom.

(6) Application examples

1. "Drumming and Passing Flowers" in Dream of Red Mansions. 2. Event bubbling in JS. 3. Processing of Encoding by Apache Tomcat in JAVA WEB, interceptor of Struts2, Filter of jsp servlet.

(7) Advantages

1. Reduce coupling. It decouples the sender and receiver of a request. 2. The object is simplified. So that the object does not need to know the structure of the chain. 3. Enhance the flexibility of assigning responsibilities to objects. By changing the members in the chain or mobilizing their order, it is allowed to dynamically add or delete responsibilities. 4. It is very convenient to add a new request processing class.

(8) Disadvantages

1. There is no guarantee that the request will be accepted. 2. The system performance will be affected to a certain extent, and it is not convenient to debug the code, which may cause circular calls. 3. It may not be easy to observe the characteristics of the runtime, which hinders debugging.

(9) Usage scenarios

1. There are multiple objects that can handle the same request, and the specific object that handles the request is automatically determined at runtime. 2. Submit a request to one of multiple objects without explicitly specifying the receiver. 3. A group of objects can be dynamically specified to process requests.

(10) Precautions

Many applications are encountered in JAVA WEB.

[3] Realization case

(1 Introduction

We create abstract class AbstractLogger with verbose logging level. Then we create three types of loggers, all extending AbstractLogger. Whether the level of each logger message belongs to its own level, if it is, it will be printed accordingly, otherwise it will not be printed and the message will be passed to the next logger.
insert image description here

(2) Create an abstract logger class


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) Create an entity class that extends the logger class

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) Create different types of loggers

Create different types of loggers. Give them different error levels and in each logger set the next logger. The next logger in each logger represents part of the chain.


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

output result

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] More case introductions

(1) Responsibility chain model solves the problem of procurement approval

1- Create the entity class of the purchase request

/**
 * @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-Create an abstract purchase approval class

/**
 * @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- Create an entity class that extends the approval class

/**
 * @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- Set up the chain of responsibility, test class

/**
 * @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) Responsibility chain mode solves the problem of data verification in the login process

The chain of responsibility model is relatively common in daily life. We usually work and deal with some affairs, and it is often a task completed by various departments in cooperation. And each department has its own responsibilities, therefore, in many cases, when a project is half-finished, it will be handed over to the next department, and the project cannot be completed until all departments have passed.

1- Create an entity class Member

@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-Login verification code in the traditional way

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- Start using the Chain of Responsibility mode optimization and create an abstract processing class Handler

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

4- Create an entity class that extends the processing class

We create non-null verification ValidateHandler class, login verification LoginHandler class and permission verification AuthHandler class respectively

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- Set up the chain at the service layer

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- Test code

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

Test Results

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

7- Responsibility chain mode + builder mode combined use optimization

Because the chain of responsibility model has a chain structure, and in the above code, we can see that the role responsible for assembling the chain structure is
MemberService. When the chain structure is long, the work of MemberService will be very cumbersome, and the code of MemberService is relatively bloated, and Subsequent changes to the processor or message type must be modified in MemberService, which does not conform to the principle of opening and closing. The reason for these problems is that the assembly of the chain structure is too complicated. For the creation of complex structures, we naturally think of the builder mode. Using the builder mode, we can automatically chain the processing node objects specified by MemberService. Assembling, the customer only needs to specify the processing node object, and does not need to care about anything else, and the order of the processing node objects specified by the customer is different, and the constructed chain structure is also different. Let's modify it and modify the code of 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;
        }
    }
}

Then, modify the code of 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的人,肯定见过
    }
}

Guess you like

Origin blog.csdn.net/weixin_44823875/article/details/128990803