Master the Chain of Responsibility Pattern in Java for Real-World Validation
This article explains the Chain of Responsibility design pattern, demonstrates its implementation with Spring‑managed handlers for product creation validation and expense‑approval workflows, shows how to configure and dynamically compose handlers via JSON, and provides complete Java code examples and diagrams.
Introduction
Reading this article you will learn how the Chain of Responsibility pattern works, how it enables workflow orchestration and dynamic extension, how to inject handlers with Spring @Resource, and how to apply recursive configuration for flexible validation.
What you will learn
Experience the power of the Chain of Responsibility pattern through concrete examples.
Implement process orchestration and dynamic extension using the pattern.
Use Spring @Resource for handler injection.
Apply recursive algorithms to build the handler chain.
Brief Overview
The Chain of Responsibility pattern assembles multiple operations into a linked chain. A request travels along the chain; each node (handler) can process the request or pass it to the next handler.
Application Scenarios
Typical scenarios include:
Operations that require a series of validations before execution.
Workflows where tasks are processed level by level.
Case 1: Multi‑Level Product Validation
Creating a product involves three steps: create, validate parameters, and save. Validation is split into several checks (null, price, stock) forming a pipeline.
Pseudocode
createProduct() {
// run a series of parameter checks; if any fails, return error
// if all pass, save the product
}The initial implementation becomes messy as validation logic grows, leading to low maintainability and code duplication.
Refactoring with the Chain of Responsibility separates each validation step into its own handler, making the code reusable and easier to extend.
UML Overview
The abstract class AbstractCheckHandler defines the template method handle() and the chain navigation method next(). Concrete handlers ( NullValueCheckHandler, PriceCheckHandler, StockCheckHandler) implement their specific validation logic.
public abstract class AbstractCheckHandler {
protected AbstractCheckHandler nextHandler;
protected ProductCheckHandlerConfig config;
public abstract Result handle(ProductVO param);
protected Result next(ProductVO param) {
if (nextHandler == null) return Result.success();
return nextHandler.handle(param);
}
}Each concrete handler checks its own rule and, if successful, calls super.next(param). Handlers can be disabled via the down flag in the configuration.
@Component
public class NullValueCheckHandler extends AbstractCheckHandler {
@Override
public Result handle(ProductVO param) {
if (config.getDown()) return super.next(param);
if (param == null) return Result.failure(ErrorCode.PARAM_NULL_ERROR);
if (param.getSkuId() == null) return Result.failure(ErrorCode.PARAM_SKU_NULL_ERROR);
// other checks …
return super.next(param);
}
}The configuration class ProductCheckHandlerConfig stores the bean name of the handler, a reference to the next handler, and a downgrade flag.
public class ProductCheckHandlerConfig {
private String handler;
private ProductCheckHandlerConfig next;
private Boolean down = Boolean.FALSE;
}Handler retrieval uses a Map<String, AbstractCheckHandler> injected by Spring. The method getHandler(config) validates the configuration, sets the handler’s config, and recursively builds the chain.
@Resource
private Map<String, AbstractCheckHandler> handlerMap;
private AbstractCheckHandler getHandler(ProductCheckHandlerConfig config) {
if (config == null) return null;
AbstractCheckHandler h = handlerMap.get(config.getHandler());
if (h == null) return null;
h.setConfig(config);
h.setNextHandler(getHandler(config.getNext()));
return h;
}The client executes the chain via HandlerClient.executeChain(handler, param). If any handler returns a failure, the chain stops and the error is propagated.
public class HandlerClient {
public static Result executeChain(AbstractCheckHandler handler, ProductVO param) {
Result r = handler.handle(param);
if (!r.isSuccess()) {
System.out.println("Chain failed: " + r);
return r;
}
return Result.success();
}
}Four test scenarios demonstrate how missing values, illegal price, or illegal stock cause the chain to abort, while a fully valid product passes all handlers and is saved.
Case 2: Expense Reimbursement Workflow
A multi‑level approval process (level‑3, level‑2, level‑1 managers) is modeled with the same pattern. Each level’s handler checks the amount range and forwards to the next level if needed. The configuration can be changed at runtime to adjust approval limits.
Pros and Cons of the Chain of Responsibility
Source Code
https://github.com/rongtao7/MyNotes
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Programmer DD
A tinkering programmer and author of "Spring Cloud Microservices in Action"
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
