Behavioral Patterns  «Prev 

Theory Behind the "Chain of Responsibility" Design Pattern

The Chain of Responsibility pattern is a behavioral design pattern that allows multiple objects to process a request without the sender needing to know which object will handle it. Instead of a single handler, multiple handlers are arranged in a chain, where each handler has the opportunity to process the request or pass it along the chain.
Key Concepts
  1. Decoupling the sender and receiver – The sender does not need to know which handler processes the request.
  2. Flexible processing structure – Each handler can either process the request or pass it to the next handler.
  3. Improved maintainability – Adding new handlers is easy without modifying existing code.

Structure
  1. Handler (Abstract Class/Interface) – Defines an interface for handling requests and an optional reference to the next handler.
  2. Concrete Handlers – Implements the handler interface, processes requests, or forwards them.
  3. Client – Creates and connects the chain, then sends requests.

Example in Java
Imagine a logging system with three levels of logging: 1)INFO, 2)DEBUG, and 3)ERROR. If a request is not handled at one level, it is passed to the next.
// Step 1: Create an abstract Handler
abstract class Logger {
    protected Logger nextLogger; // Reference to the next logger in the chain

    public void setNextLogger(Logger nextLogger) {
        this.nextLogger = nextLogger;
    }

    public void logMessage(String message, LogLevel level) {
        if (canHandle(level)) {
            write(message);
        } else if (nextLogger != null) {
            nextLogger.logMessage(message, level);
        }
    }

    protected abstract boolean canHandle(LogLevel level);
    protected abstract void write(String message);
}

// Step 2: Define Log Levels
enum LogLevel {
    INFO, DEBUG, ERROR
}

// Step 3: Create Concrete Handlers
class InfoLogger extends Logger {
    @Override
    protected boolean canHandle(LogLevel level) {
        return level == LogLevel.INFO;
    }

    @Override
    protected void write(String message) {
        System.out.println("[INFO] " + message);
    }
}

class DebugLogger extends Logger {
    @Override
    protected boolean canHandle(LogLevel level) {
        return level == LogLevel.DEBUG;
    }

    @Override
    protected void write(String message) {
        System.out.println("[DEBUG] " + message);
    }
}

class ErrorLogger extends Logger {
    @Override
    protected boolean canHandle(LogLevel level) {
        return level == LogLevel.ERROR;
    }

    @Override
    protected void write(String message) {
        System.out.println("[ERROR] " + message);
    }
}

// Step 4: Set Up the Chain and Test
public class ChainOfResponsibilityDemo {
    public static void main(String[] args) {
        // Creating the chain of loggers
        Logger errorLogger = new ErrorLogger();
        Logger debugLogger = new DebugLogger();
        Logger infoLogger = new InfoLogger();

        // Setting up the chain
        infoLogger.setNextLogger(debugLogger);
        debugLogger.setNextLogger(errorLogger);

        // Test the chain with different log levels
        infoLogger.logMessage("This is an informational message.", LogLevel.INFO);
        infoLogger.logMessage("This is a debug message.", LogLevel.DEBUG);
        infoLogger.logMessage("This is an error message.", LogLevel.ERROR);
    }
}
How It Works
  1. The client sends a log request starting at the first handler (InfoLogger).
  2. If the InfoLogger can handle the request (INFO level), it logs the message.
  3. If not, it forwards the request to the next logger (DebugLogger), and so on.
  4. If no handler can process the request, it is ignored.
Output
[INFO] This is an informational message.
[DEBUG] This is a debug message.
[ERROR] This is an error message.

Use Cases of Chain of Responsibility
  • Event Handling (GUI frameworks)
  • Authentication Middleware (Processing security layers)
  • Logging Frameworks
  • Approval Workflows (Loan approvals, leave requests)

Advantages
  • Decouples sender and receiver
  • Flexible processing flow
  • Easy to extend and modify

Disadvantages
  • Debugging complexity – Harder to track request flow
  • Potential performance issues – If the chain is long, processing may be slow
Chain.java: This is the interface that acts as a chain link.
package com.java.behavioral.chainofresponsibility;
public interface Chain {
 public abstract void setNext(Chain nextInChain);
 public abstract void process(Number request);
}

Number.java: This class represents the request object.

package com.java.behavioral.chainofresponsibility;
public class Number {
 private int number;
 public Number(int number) {
  this.number = number;
 }
 public int getNumber() {
  return number;
 }
}

NegativeProcessor.java: This class acts as the link in the chain series.
package com.java.behavioral.chainofresponsibility;
public class NegativeProcessor implements Chain {
 private Chain nextInChain;
 public void setNext(Chain c) {
  nextInChain = c;
 }
 public void process(Number request) {
  if(request.getNumber() < 0) {
   System.out.println("NegativeProcessor : " + request.getNumber());
  } else {
    nextInChain.process(request);
  }
 }
}

ZeroProcessor.java: This class is another link in the chain series.
package com.java.behavioral.chainofresponsibility;

public class ZeroProcessor implements Chain {
 private Chain nextInChain;
 public void setNext(Chain c) {
  nextInChain = c;
 }
 public void process(Number request) {
  if (request.getNumber() == 0) {
   System.out.println("ZeroProcessor : " + request.getNumber());
  } else{
   nextInChain.process(request);
  }
 }
}

PositiveProcessor.java: This class represents another link in the chain series.
package com.java.behavioral.chainofresponsibility;
public class PositiveProcessor implements Chain {
 private Chain nextInChain;
 public void setNext(Chain c) {
  nextInChain = c;
 }
 public void process(Number request) {
  if(request.getNumber() > 0) {
   System.out.println("PositiveProcessor : " + request.getNumber());
	}else {
   nextInChain.process(request);
	}
 }
}

TestChain.java: This class configures the chain of responsibility and executes it.
package com.java.behavioral.chainofresponsibility;
public class TestChain {
 public static void main(String[] args) {
  //configure Chain of Responsibility
  Chain c1 = new NegativeProcessor();
  Chain c2 = new ZeroProcessor();
  Chain c3 = new PositiveProcessor();
  c1.setNext(c2);
  c2.setNext(c3);
  //calling chain of responsibility
  c1.process(new Number(99));
  c1.process(new Number(-30));
  c1.process(new Number(0));
  c1.process(new Number(100));
 }
}