An introduction to the Chain of Responsibility pattern of Java design pattern

  • 2020-04-01 03:42:12
  • OfStack

Definition of Chain of Responsibility: Chain of Responsibility(CoR) is an attempt to process a request with a set of classes. These classes are loosely coupled, and the only common thing is to pass requests between them. That is to say, A request comes in, class A handles it first, if not, it passes to class B, if not, it passes to class C, and so on like A chain.

How to use the responsibility chain pattern
Even though this is how CoR is used, it also demonstrates what CoR is.

There is a Handler interface:


public interface Handler{
  public void handleRequest();
}

This is an example of handling requests if there are multiple requests, such as request for help request printing or request formatting:
In pieces the solution that comes to mind first is: Add multiple requests to the interface:

public interface Handler{
  public void handleHelp();
  public void handlePrint();
  public void handleFormat();
}

Specifically, it is a section of implementation interface Handler code:


public class ConcreteHandler implements Handler{
  private Handler successor;
  public ConcreteHandler(Handler successor){
          this.successor=successor;
        }   public void handleHelp(){
    //The code that specifically handles the request Help is
    ...
  }   public void handlePrint(){
    //If it's print go to print
    successor.handlePrint();
  }
  public void handleFormat(){
    //If Format goes to Format
    successor.handleFormat();
  } }

There are three of these concrete implementation classes, the one that handles help, the one that handles Print and the one that handles Format which is probably the most common programming idea.

Although the idea is simple and straightforward, there is an extension problem, if we need to add another request type, we need to modify the interface and each of its implementations.

In pieces the second plan: We turn each request into an interface, so we have the following code:


public interface HelpHandler{
  public void handleHelp();
} public interface PrintHandler{
  public void handlePrint();
} public interface FormatHandler{
  public void handleFormat();
} public class ConcreteHandler
  implements HelpHandler,PrintHandler,FormatHandlet{
  private HelpHandler helpSuccessor;
  private PrintHandler printSuccessor;
  private FormatHandler formatSuccessor;   public ConcreteHandler(HelpHandler helpSuccessor,PrintHandler printSuccessor,FormatHandler             formatSuccessor)
  {
    this.helpSuccessor=helpSuccessor;
    this.printSuccessor=printSuccessor;
    this.formatSuccessor=formatSuccessor;
  }   public void handleHelp(){
    .......
  }   public void handlePrint(){this.printSuccessor=printSuccessor;}   public void handleFormat(){this.formatSuccessor=formatSuccessor;} }

In the case of adding a new request, this approach only saves the amount of changes to the interface that the interface implementation ConcreteHandler needs to make. And the code is clearly not simple and beautiful.


In pieces solution 3: Only one parameterized method is used in the Handler interface:


public interface Handler{
  public void handleRequest(String request);
}
then Handler The implementation code is as follows: public class ConcreteHandler implements Handler{
  private Handler successor;   public ConcreteHandler(Handler successor){
    this.successor=successor;
  }   public void handleRequest(String request){
    if (request.equals("Help")){
      //Here is the specific code to handle Help
    }else
      //Pass to the next
      successor.handle(request);     }
  } }

So let's say request is of type String, but what if it's not? Of course, we can create a special class Request

In pieces final solution: The code of interface Handler is as follows:


public interface Handler{
  public void handleRequest(Request request);
}
Request Class definition: public class Request{
  private String type;   public Request(String type){this.type=type;}   public String getType(){return type;}   public void execute(){
    //Request really concrete behavior code
  }
}

Then the Handler implementation code is as follows:

public class ConcreteHandler implements Handler{
  private Handler successor;   public ConcreteHandler(Handler successor){
    this.successor=successor;
  }   public void handleRequest(Request request){
    if (request instanceof HelpRequest){
      //Here is the specific code to handle Help
    }else if (request instanceof PrintRequst){
      request.execute();
    }else
      //Pass to the next
      successor.handle(request);     }
  } }

The solution is CoR, which has classes of Responsibility on a Chain, so it's called Chain of Responsibility.

1. The advantage of CoR: because it is impossible to predict which type of request from the outside world belongs to, if each class encounters a request that it cannot handle, it can just give up. This certainly reduces the coupling between classes.
2. The disadvantage of CoR is that it is inefficient, because the completion of a request may have to be traversed to the end. Of course, it can also be optimized using the concept of tree. In Java AWT1.0, the mouse button thing was handled by CoR, and after java.1.1, the Observer was used instead of CoR.

Extensibility is poor, because in CoR, there must be a unified interface Handler.


Related articles: