跪拜 Guibai
← All articles
Backend

Chain of Responsibility Meets Strategy: A Cleaner Way to Slash if-else Sprawl

By 神奇小汤圆 ·
Read original on juejin.cn ↗ Google Translate ↗ Alt translation

Monolithic service methods packed with conditional logic are the fastest way to make a codebase brittle. This dual-pattern approach keeps validation and approval pipelines open for extension but closed for modification, which directly lowers the risk of regressions when business rules change.

Summary

A multi-step validation flow, such as listing a product on an e-commerce platform, can quickly turn into a thicket of if-else checks. The Chain of Responsibility pattern solves this by linking independent handler objects into an ordered pipeline. Each handler inspects the request and either processes it, passes it on, or throws an exception to halt the chain. A Spring-based context automatically discovers handlers by their mark and order, so adding a new validation step means writing one new class — no existing code changes required.

Layering the Strategy pattern on top gives each handler its own pluggable algorithm. The chain controls the sequence and routing; the strategy defines what a particular handler actually does. This separation means the same chain structure can be reused for different business flows just by swapping strategies, without touching the pipeline logic.

The result is a codebase where business rules live in small, single-responsibility classes. Flow control is centralized in the chain context, and individual processing logic is isolated in strategies. When requirements shift — a new approval level, a different validation rule — the change is a new class or a strategy swap, not a rewrite of a monolithic method.

Takeaways
Each handler in the chain implements a common interface with a mark() identifier and a getOrder() priority, letting the framework auto-discover and sequence them.
A Spring CommandLineRunner scans all handler beans at startup, groups them by mark, and sorts them by order — no manual chain wiring.
Handlers can halt the chain by throwing an exception, so downstream steps never run on invalid data.
Strategy objects encapsulate the actual processing algorithm; a handler delegates to a strategy, making the handler’s behavior swappable at runtime.
Adding a new validation step or approval level requires only a new handler class and, if needed, a new strategy — existing code stays untouched.
The combined pattern eliminates deep if-else or switch-case blocks by distributing decisions across focused, single-responsibility classes.
Conclusions

Most discussions treat Chain of Responsibility and Strategy as alternatives, but they solve orthogonal problems: one routes requests, the other encapsulates algorithms. Combining them produces a pipeline where both the sequence and the behavior at each stop are independently configurable.

The Spring auto-discovery mechanism turns the chain into a plugin architecture. A developer can drop a new handler into the classpath and it joins the chain automatically — no configuration file, no factory method update.

Using exceptions as a chain-break signal is pragmatic but controversial. It works cleanly for validation pipelines where failure means stop, but it muddies the water if partial processing or rollback is ever needed.

The pattern’s real payoff isn’t the first implementation — it’s the tenth change request. When a product manager asks for a new check between step 3 and step 4, the answer is a single new class with a carefully chosen order number.

Concepts & terms
Chain of Responsibility Pattern
A behavioral design pattern that passes a request along a chain of handler objects. Each handler decides whether to process the request or pass it to the next handler, decoupling the sender from the receiver.
Strategy Pattern
A behavioral design pattern that defines a family of interchangeable algorithms, encapsulates each one, and lets the algorithm vary independently from the clients that use it.
CommandLineRunner (Spring)
A Spring Boot interface with a single run() method that executes after the application context is loaded. Often used to scan and register beans for dynamic chains or pipelines at startup.
Ordered Interface (Spring)
A Spring interface that assigns a numerical order to beans. Lower values have higher priority. Used here to control the sequence of handlers in a responsibility chain.
Source: juejin.cn ↗ Google Translate ↗ Backup ↗