Chain of Responsibility
The problem
Before the shop accepts an order it must pass a series of checks: is everything in stock? is there
a shipping address? does it look fraudulent? A single validateOrder method holding all the rules
becomes a wall of ifs where the rules can’t be reordered, reused, or tested independently – and
every new rule means editing (and re-risking) the same method.
The pattern
Each check is a self-contained handler; handlers are linked into a chain and each either rejects the request or passes it along:
OrderCheck validation = OrderCheck.chainOf(List.of(
new StockCheck(inStock), new AddressCheck(), new FraudCheck()));
ValidationResult result = validation.validate(request);
This is the servlet-filter / middleware shape: the pipeline is configuration, the steps are components.
Benefits
- Steps are independent – each check is a small class with one job, tested on its own.
- The pipeline is data – add, remove, or reorder checks by editing the list, not the logic.
- Short-circuiting for free – a rejection stops the chain; later (possibly expensive) checks never run.
- Separation of what checks exist from which run – different channels (web, phone, trade) can each assemble their own chain from the same check classes. (One honest limitation of the linked-handler shape: each handler instance stores its successor, so it belongs to one chain at a time – give each chain its own instances. An immutable list-based pipeline avoids this at the cost of a less canonical shape.)
Seen in the wild
Servlet Filter chains, Spring Security’s filter chain, Spring MVC interceptors, Netty’s
ChannelPipeline, logging frameworks passing events up the logger hierarchy.
Implementation
OrderCheck– the handler interface, including the chain assembly.StockCheck,AddressCheck,FraudCheck– the concrete checks.OrderRequest,ValidationResult– the request and result types.