DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
  1. DZone
  2. Coding
  3. Languages
  4. Chain of Responsibility Design Pattern in Java: 2 Implementations

Chain of Responsibility Design Pattern in Java: 2 Implementations

Learn about two implementations of the Chain of Responsibility design pattern in Java: one versus multiple request handlers serving the input request.

Arpan Das user avatar by
Arpan Das
·
Jan. 10, 18 · Tutorial
Like (27)
Save
Tweet
Share
32.39K Views

Join the DZone community and get the full member experience.

Join For Free

The Chain of Responsibility (COR) design pattern is used when more than one object handles a request and performs their corresponding responsibilities to complete the whole task.

The pattern can be used to achieve loose coupling in software design, where the request can be passed through a chain of objects or request handler for processing. Based on some criteria in each handler object, it will handle the request or pass it to the next handler. Following is a representation of the COR pattern:

Image title

The COR pattern can be implemented in two ways.

1. One Request Handler Will Serve the Input Request

The request initiator is not aware of the exact handler that will serve its request. Every handler in the chain will have the responsibility to decide if they can serve the request. If any handler decides to forward the request, it should be capable of choosing the next handler and forwarding it. There is a possible scenario in which none of the handlers may serve the request.

This type of COR pattern implementation is present in Java’s exception handling mechanism. We can have multiple catch blocks in a try-catch block code. Here, every catch block is kind of a handler to handle that particular exception.

When an exception occurs in the try block, it sends the exception to the first catch block to process. If the catch block is not able to handle it, it forwards the exception to the next handler in the chain, i.e. the next catch block. If even the last catch block is not able to handle it, then the exception is thrown outside of the chain to the calling program.

2. Multiple Request Handlers Will Serve the Input Request

There is another type of COR pattern implementation in which more than one handler participates in the processing to complete the whole task. In this implementation, each handler performs its part of the task (if required based on some condition) and forwards the request to the next handler. The whole task is completed once the request travels through the complete chain.

Spring’s HandlerInterceptorAdapter is an example of this type of COR pattern. The HandlerInterceptorAdapter defines three methods: preHandle(), postHandle(), and afterCompletion(). Here, the preHandle() follows the COR pattern if we have multiple interceptors for intercepting a request. Then, the request flows through all the preHandle() methods sequentially, and each preHandle() can perform some task and modify the request before it reaches to the controller. Here’s what a simple preHandle() implementation will look like:

@Override

public boolean preHandle(

  HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception {

    // your code

    return true;

}

This method is called before handling a request. If it returns true, the Spring framework sends the request further to the next handler method (to the next interceptor’s preHandle). If the method returns false, Spring assumes that request has been handled, and no further processing is required. By this boolean flag, each handler of the chain chooses the next handler. Now, let's consider a simple implementation of COR pattern.

We have a request that needs to collect data from different external third-party data storages like a database, file storage, and cloud environments. We will develop a chain of such data collectors that are responsible for collecting data from different sources. First, we will define the type of RequestHandler.

This is an interface defining the basic blueprint of all the concrete RequestHandlers.

package com.tuturself.cor;

public interface RequestHandler {

void setNextRequestHandler(RequestHandler requestHandler);

        boolean process(RequestData requestData);
}

Now, we will define the three concrete request handlers that are responsible for collecting data from each of the sources (database, file, and cloud). In addition, each handler has a pointer to the next handler object so that control can be passed to the next handler when the current handler finishes.

DBDataHandler.java:

package com.tuturself.cor;

public class DBDataHandler implements RequestHandler {

    private RequestHandler requestHandler;

    @Override
    public void setNextRequestHandler(RequestHandler requestHandler) {
        this.requestHandler = requestHandler;

    }

    @Override
    public boolean process(RequestData requestData) {
        requestData.setMetaDBData("Meta Data from DB is populated");
        return requestHandler == null ? true : requestHandler.process(requestData);
    }
}

FileDataHandler.java:

package com.tuturself.cor;

public class FileDataHandler implements RequestHandler {

    private RequestHandler requestHandler;

    @Override
    public void setNextRequestHandler(RequestHandler requestHandler) {
        this.requestHandler = requestHandler;

    }

    @Override
    public boolean process(RequestData requestData) {
        requestData.setMetaFileData("Meta Data from File is populated");
        return requestHandler == null ? true : requestHandler.process(requestData);
    }
}

CloudDataHandler.java:

package com.tuturself.cor;

public class CloudDataHandler implements RequestHandler {

    private RequestHandler requestHandler;

    @Override
    public void setNextRequestHandler(RequestHandler requestHandler) {
        this.requestHandler = requestHandler;

    }

    @Override
    public boolean process(RequestData requestData) {
        requestData.setMetaCloudData("Meta Data from Cloud is populated");
        return requestHandler == null ? true : requestHandler.process(requestData);
    }
}

Now, we will have a domain object that carries data through the chain of handlers named RequestData.

package com.tuturself.cor;

public class RequestData {

    private String metaDBData;
    private String metaFileData;
    private String metaCloudData;

    public String getMetaDBData() {
        return metaDBData;
    }

    public void setMetaDBData(String metaDBData) {
        this.metaDBData = metaDBData;
    }

    public String getMetaFileData() {
        return metaFileData;
    }

    public void setMetaFileData(String metaFileData) {
        this.metaFileData = metaFileData;
    }

    public String getMetaCloudData() {
        return metaCloudData;
    }

    public void setMetaCloudData(String metaCloudData) {
        this.metaCloudData = metaCloudData;
    }

    @Override
    public String toString() {
        return "RequestData [metaDBData=" + metaDBData + ",\n " +
            "metaFileData=" + metaFileData + ",\n metaCloudData=" +
            metaCloudData + "]";
    }
}

Now, let's test the code. Here, we are creating an object of each handler type, then creating a chain of handlers, and finally calling the process method of the first handler object in the chain. The first handler will call the next handler when it is finished, and this step will be continued until the chain is finished.

package com.tuturself.cor;

import java.util.*;

public class TestCOR {

    public static void main(String[] args) {
        RequestData requestData = new RequestData();
        List < RequestHandler > requestHandlers = new ArrayList < > ();
        requestHandlers.add(new DBDataHandler());
        requestHandlers.add(new FileDataHandler());
        requestHandlers.add(new CloudDataHandler());
        // create the chain of Handler
        for (int i = 0; i < requestHandlers.size() - 1; i++) {
            requestHandlers.get(i).setNextRequestHandler(requestHandlers.get(i + 1));
        }
        requestHandlers.get(0).process(requestData);
        System.out.println(requestData);
    }
}

Output:

RequestData [metaDBData=Meta Data from DB is populated,
 metaFileData=Meta Data from File is populated,
 metaCloudData=Meta Data from Cloud is populated]

Benefits of the chain of responsibility pattern:

  1. Decouples the sender and receivers of the request.
  2. Simplifies our object because it doesn't have to know the chain’s structure and keep direct references to its members.
  3. Allows us to add or remove responsibilities dynamically by changing the members or order of the chain.

Drawbacks of the chain of responsibility pattern:

  1. Execution of the request isn’t guaranteed; it may fall off the end of the chain if no object handles it.
  2. It can be hard to observe and debug the runtime characteristics.
Requests Implementation Design Java (programming language) Object (computer science) Spring Framework Blocks

Published at DZone with permission of Arpan Das. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Explainer: Building High Performing Data Product Platform
  • PHP vs React
  • Differences Between Site Reliability Engineer vs. Software Engineer vs. Cloud Engineer vs. DevOps Engineer
  • Visual Network Mapping Your K8s Clusters To Assess Performance

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: