Skip to content

Sequential Request Processing

Pass a request along a chain until one handler takes it

Builds on OCP - The Open/Closed Principle

Stage 3core2 min read

The refactored code uses the Chain of Responsibility Design Pattern. A request passes through a line of separate handler objects until one of them processes it. This removes the rigid branching, and the sender no longer depends on the receivers.


1. How This Approach Solves the Issues

We replace the one big routing method with a chain of independent handler classes. Each class extends the same base class, so they can be used interchangeably (polymorphism). The code is split into small parts and is easy to extend.

IssueSolution with Chain of Responsibility
Violation of Open-Closed PrincipleTo add a handler (e.g., FraudSupport or EscalationSupport), create a new class that extends SupportHandler and link it into the chain. The existing handler classes stay unchanged.
Monolithic CodeEach handler class holds only its own logic. This follows the Single Responsibility Principle (SRP): one class, one job.
Scalability and Flexibility BottlenecksYou can change the order while the program runs by changing how handlers are linked with setNextHandler(). This lets you build custom routes.

2. Key Components in the Solution

  • Handler (SupportHandler): An abstract base class. It holds a reference to the next handler (nextHandler) and declares the handleRequest() method that every handler must have.
  • Concrete Handlers (GeneralSupport, BillingSupport, etc.): Separate classes that check whether the incoming request type is theirs. If it is, they handle it. If not, they pass it to nextHandler.
  • Client (Main): Builds the chain and sends requests to the first handler. It does not need to know which handler will finally process them.

3. When to Use the Chain of Responsibility Pattern

  • Multiple Candidate Handlers: When more than one object could process a request, and you cannot tell which one when you write the code.
  • Dynamic Pipeline Configuration: When you need to arrange, reorder or change the handlers while the program runs.
  • Decoupling Sender and Receivers: When you want to send a request to several possible receivers without tying the sender to specific receiver classes.
  • Elimination of Monolithic Conditionals: When one dispatcher method has large if-else or switch blocks that decide which part of the system does the work.

4. Summary of the Architectural Flow

Think of a phone menu at a support center. You dial in, and your call first goes to the General Inquiries desk. If it is a billing question, they transfer you to Billing. If Billing cannot solve it, they pass you to Technical Support. You dial one number, and the company forwards the call inside until the right department answers.

In this topic

After code1 file

After/Main.java125 linesGitHub
package DesignPatterns.BehaviouralPatterns.ChainOfResponsibilityPattern.After;import java.util.*;// ============================================================================// 1. ABSTRACT BASE HANDLER// ============================================================================/** * Defines the unified contract for all links in the processing pipeline. * Holds a reference to the next handler in the sequence and provides a  * mechanism to build the chain dynamically. */abstract class SupportHandler {    // Reference pointer to the next processing unit in the chain    protected SupportHandler nextHandler;    /**     * Builds or mutates the chain link at runtime.     * Allows dynamic reordering or insertion of handlers without class rewrites.     */    public void setNextHandler(SupportHandler nextHandler) {        this.nextHandler = nextHandler;    }    /**     * Abstract method encapsulating the handling contract.     * Subclasses either consume the request or pass it along via nextHandler.     */    public abstract void handleRequest(String requestType);}// ============================================================================// 2. CONCRETE HANDLERS// ============================================================================/** * Concrete handler for basic/general inquiries. * If the request does not match its domain, it forwards the request forward. */class GeneralSupport extends SupportHandler {    @Override    public void handleRequest(String requestType) {        if (requestType.equalsIgnoreCase("general")) {            System.out.println("GeneralSupport: Handling general query");        } else if (nextHandler != null) {            // Forward the request to the next link in the chain            nextHandler.handleRequest(requestType);        }    }}/** * Concrete handler for refund and billing-related requests. */class BillingSupport extends SupportHandler {    @Override    public void handleRequest(String requestType) {        if (requestType.equalsIgnoreCase("refund")) {            System.out.println("BillingSupport: Handling refund request");        } else if (nextHandler != null) {            // Pass along if not responsible for this request type            nextHandler.handleRequest(requestType);        }    }}/** * Concrete handler for technical bugs and system troubleshooting. */class TechnicalSupport extends SupportHandler {    @Override    public void handleRequest(String requestType) {        if (requestType.equalsIgnoreCase("technical")) {            System.out.println("TechnicalSupport: Handling technical issue");        } else if (nextHandler != null) {            // Pass down the chain            nextHandler.handleRequest(requestType);        }    }}/** * Concrete handler for shipping and delivery queries. * Acts as the final link in this configuration, handling unhandled fallbacks. */class DeliverySupport extends SupportHandler {    @Override    public void handleRequest(String requestType) {        if (requestType.equalsIgnoreCase("delivery")) {            System.out.println("DeliverySupport: Handling delivery issue");        } else if (nextHandler != null) {            nextHandler.handleRequest(requestType);        } else {            // Terminal fallback when no link in the chain can process the request            System.out.println("DeliverySupport: No handler found for request: " + requestType);        }    }}// ============================================================================// 3. CLIENT CODE & CHAIN CONFIGURATION// ============================================================================public class Main {    public static void main(String[] args) {        // Step 1: Instantiate individual concrete handlers independently        SupportHandler general = new GeneralSupport();        SupportHandler billing = new BillingSupport();        SupportHandler technical = new TechnicalSupport();        SupportHandler delivery = new DeliverySupport();        // Step 2: Assemble the execution pipeline dynamically        // General -> Billing -> Technical -> Delivery        general.setNextHandler(billing);        billing.setNextHandler(technical);        technical.setNextHandler(delivery);        // Step 3: Trigger requests starting from the head of the chain        System.out.println("--- Request 1: refund ---");        general.handleRequest("refund");        System.out.println("\n--- Request 2: delivery ---");        general.handleRequest("delivery");        System.out.println("\n--- Request 3: unknown ---");        general.handleRequest("unknown");    }}

Done with this topic?

How well do you know it?

PreviousIterator Pattern: Sequential Access Without Exposure