Behavioral PatternsChain of ResponsibilityHard⏱️ ~4 min

Chain of Responsibility: Interview Deep Dive

Common Interview Questions

Question 1: How do you prevent infinite loops in the chain?

Answer: Infinite loops occur if the chain forms a cycle, such as HandlerA points to HandlerB, which points back to HandlerA. To prevent this, follow these approaches:

First, ensure that setNext() only allows setting the next handler once or validates that the handler being added is not already in the chain. Use a visited set to detect cycles during chain construction.

Second, design handlers to always move forward. A handler should either process the request and stop, or forward it to the next handler without looping back. Never allow a handler to call a previous handler in the chain.

Third, add a maximum chain length limit or traversal counter. If a request passes through more than N handlers (for example, 50), throw an exception assuming a configuration error exists.

Question 2: Should a handler always forward to the next handler, or can it stop the chain?

Answer: It depends on the use case. There are two valid approaches:

Stop on First Match: Used in authentication chains or validation pipelines. Once a handler successfully processes the request, the chain stops. For example, if Token Authentication succeeds, do not try Basic Authentication. Implement this by simply not calling nextHandler.handle() after successful processing.

Process and Continue: Used in logging or middleware pipelines. Each handler does its work and then forwards the request regardless. For example, a logging handler records the request and always passes it forward. Implement this by calling nextHandler.handle() after processing, not instead of processing.

The key is consistency. All handlers in a chain should follow the same forwarding convention, otherwise behavior becomes unpredictable.

Question 3: How do you handle requests that no handler processes?

Answer: This is a critical edge case. Use one of these strategies:

Default Handler: Place a catch-all handler at the end of the chain that always processes requests. For example, in an approval workflow, the final handler could be an administrator approval that accepts any request amount. This guarantees every request gets handled.

Explicit Validation: Have the client check if the request was processed after calling the chain. The WithdrawalRequest in the ATM example could have an isFullyProcessed() method that returns false if any amount remains. The client then throws an exception or displays an error message.

Null Object Pattern: Instead of allowing nextHandler to be null, use a NullHandler that does nothing. This eliminates null checks throughout the chain, but you still need to track if actual processing occurred.

Interview Tip: When answering this question, emphasize that the choice depends on domain requirements. In payment processing, unhandled requests are critical errors requiring exceptions. In content filtering, unhandled requests might be acceptable and simply pass through unchanged. Always tie your answer to the business context.

Machine Coding Considerations

Chain Builder Pattern: In machine coding rounds, you may be asked to implement a fluent interface for building chains. This improves readability:

ChainBuilder.start()
  .addHandler(new AuthenticationHandler())
  .addHandler(new AuthorizationHandler())
  .addHandler(new RateLimitHandler())
  .build()

The builder internally calls setNext() on each handler, maintaining the chain structure. It can also validate the chain (for example, ensure no null handlers, detect cycles).

Handler Registry: For configurable chains, implement a registry that maps handler names to instances:

HandlerRegistry.register("auth", new AuthenticationHandler())
HandlerRegistry.register("rateLimit", new RateLimitHandler())

chain = ChainBuilder.fromConfig(["auth", "rateLimit"])

This allows runtime configuration through configuration files or databases, a common requirement in enterprise applications.

Request Context Object: Instead of passing a simple request, use a rich context object that accumulates state as it moves through the chain:

class RequestContext:
  originalRequest: Request
  metadata: Map<string, any>
  processingHistory: List<HandlerResult>

  addMetadata(key, value)
  recordHandlerResult(handlerName, success, message)

This provides visibility into which handlers processed the request and allows handlers to share information without tight coupling. For example, an authentication handler might add user information to metadata that an authorization handler later consumes.

Common Variations

Conditional Chain: Handlers decide whether to forward based on request properties, not just whether they handled it. For example, a handler might say "I processed this request, but it still needs further handling, so I will forward it." This requires returning a result object (for example, HandlerResult with shouldContinue flag).

Bidirectional Chain: Handlers maintain references to both next and previous handlers, allowing requests to flow backward. This is useful in undo/redo scenarios or when a handler needs to send feedback to the previous handler. However, this increases complexity significantly and often indicates that Mediator would be more appropriate.

Priority-Based Chain: Instead of a fixed linear order, handlers have priorities, and the chain is dynamically sorted. This allows flexible handler ordering without manual reordering. Implement using a priority queue or sorted list during chain construction.

Testing Strategy

Unit Test Individual Handlers: Mock the next handler and verify that your handler correctly processes requests it can handle and forwards requests it cannot. Test both the processing logic and the forwarding logic independently.

Integration Test Full Chains: Build complete chains and verify end-to-end behavior. Test scenarios where the first handler processes, middle handler processes, last handler processes, and no handler processes. Verify the final state matches expectations.

Test Edge Cases: Empty chain (no handlers), single handler chain, very long chain (performance), null requests, requests that partially match multiple handlers.

Interview Tip: If asked to implement this pattern in a machine coding round, start with the simplest version: handler interface, one concrete handler, basic chain building. Then incrementally add features like fluent builders, default handlers, or request context objects based on time remaining. Demonstrating iterative development shows strong engineering judgment.
💡 Key Takeaways
Prevent infinite loops through cycle detection during chain construction and forward-only design
Handlers can stop on first match (auth/validation) or process and continue (logging/middleware)
Handle unprocessed requests with default handlers, explicit validation, or null object pattern
Use ChainBuilder for fluent API and HandlerRegistry for runtime configuration
Test individual handlers with mocks and full chains with integration tests covering edge cases
📌 Examples
1Chain builder with fluent API for readable chain construction
2Request context object accumulating metadata as it flows through handlers
3Priority-based chain where handler order is determined dynamically
4Bidirectional chain for undo/redo scenarios with forward and backward links
← Back to Chain of Responsibility Overview
Chain of Responsibility: Interview Deep Dive | Chain of Responsibility - System Overflow