Mastering the Chain of Responsibility Pattern: From Classic Story to Front‑End Code
This article explains the Chain of Responsibility design pattern, illustrates it with a historical Three Kingdoms story, shows progressive refactorings from procedural code to abstract handlers in TypeScript, and demonstrates practical front‑end scenarios such as flexible location retrieval.
Overview
Explain what the Chain of Responsibility pattern is and the problem it solves.
Introduce a short story, code it, and iteratively apply the pattern.
Present a real front‑end business requirement and implement it using the pattern.
What Is the Chain of Responsibility Pattern?
According to the classic Design Patterns: Elements of Reusable Object‑Oriented Software , the pattern allows multiple objects a chance to handle a request, avoiding tight coupling between sender and receiver by linking the objects into a chain and passing the request along until one handles it.
Multiple objects get the opportunity to process a request, thereby decoupling the sender from the receiver; the objects are linked in a chain and the request travels along the chain until an object handles it.
Three Kingdoms Story: Warm‑Wine Slaying Hua Xiong
The well‑known tale of "Warm‑Wine Slaying Hua Xiong" is used as a narrative backdrop.
In the initial procedural version (v1), the code uses nested if statements to simulate each general fighting Hua Xiong:
// Note: naming with pinyin is for demo only; avoid in real projects
function killHuaXiong() {
let win = false;
// round 1: General A vs Hua Xiong
win = APKHuaXiong();
if (!win) {
// round 2: General B vs Hua Xiong
win = BPKHuaXiong();
if (!win) {
// round 3: General C vs Hua Xiong
win = CPKHuaXiong();
if (!win) {
// round 4: Guan Yu vs Hua Xiong
win = DPKHuaXiong();
}
}
}
return win;
}Problems with this approach:
Q1: If Guan Yu also loses, the function must be heavily modified—poor extensibility.
Q2: Changing the order of generals requires major rewrites.
Refactoring with the Chain of Responsibility (v2)
The code is restructured using an abstract handler interface and concrete handlers:
abstract class IHandler {
protected nextHandler: IHandler | null;
setNextHandler(handler: IHandler): void {
this.nextHandler = handler;
}
abstract handle(): boolean;
}
class AHandler extends IHandler {
override handle() {
let canHandle = false;
console.log('A loses');
if (!canHandle && this.nextHandler) {
return this.nextHandler.handle();
}
return canHandle;
}
}
class GuanYuHandler extends IHandler {
override handle() {
let canHandle = false;
console.log('Guan Yu wins');
// Guan Yu can handle Hua Xiong
canHandle = true;
if (!canHandle && this.nextHandler) {
return this.nextHandler.handle();
}
return canHandle;
}
}
const aHandlerIns = new AHandler();
// similarly create B, C, GuanYu handlers
// chain them
aHandlerIns.setNextHandler(bHandlerIns);
bHandlerIns.setNextHandler(cHandlerIns);
cHandlerIns.setNextHandler(guanYuHandlerIns);
// start the chain
aHandlerIns.handle();This version solves the earlier issues: adding a new handler only requires creating a class and linking it, while reordering handlers only changes the chain configuration.
Further Abstraction (v3)
Common logic is moved to the abstract base class, leaving only the specific handling in subclasses:
abstract class IHandler {
// common code lifted to abstract base class
handle(): boolean {
const canHandle = this.doHandle();
if (!canHandle && this.nextHandler) {
return this.nextHandler.handle();
}
return canHandle;
}
abstract doHandle(): boolean;
}
class AHandler extends IHandler {
override doHandle(): boolean {}
}A manager class could further orchestrate handler ordering, eliminating direct coupling between handlers.
Re‑examining the Definition
The pattern focuses on being able to handle a problem rather than which object ultimately does it.
Knowledge Transfer – Analogy to Linked Lists
The pattern resembles a linked list: Handler is analogous to a Node. nextHandler corresponds to the next pointer.
Front‑End Application Scenarios
In typical front‑end projects, obtaining geographic location can be done via multiple strategies (URL parameters, cached localStorage data, app‑specific APIs, WeChat SDK, Baidu SDK, etc.). The requirement is satisfied as soon as any strategy succeeds, making the Chain of Responsibility a natural fit.
URL parameters: /path?lat=110&lon=123 (app environment)
Valid cached latitude/longitude in localStorage
App‑specific location capability (app environment)
WeChat SDK wx.getLocation (WeChat environment)
Baidu SDK
…
These handlers are linked into a chain that stops when one returns a location.
Sample TypeScript implementation:
abstract class ILocationHandler {
async handle(): Promise<Partial<TResult>> {}
// Some methods can only be used in specific environments, e.g., app's position action
abstract canIUse(): boolean;
abstract doHandle(): Promise<Partial<TResult>>;
}
class UrlParamsLocationHandler extends ILocationHandler {
override canIUse() { return true; }
override doHandle() { return mockUrlParamsResolver(); // actual location fetch
}
}
// LocationChain is a linked list of ILocationHandler instances
class LocationChain {
// ...
append(handler: ILocationHandler) {}
async execute() {}
}Full source code is linked in the references.
Final Thoughts
Many developers lack a solid grasp of OOP concepts. Applying well‑known design patterns like the Chain of Responsibility in appropriate business and engineering contexts leads to more extensible and maintainable code.
References
[1]Design Patterns: Elements of Reusable Object‑Oriented Software – https://book.douban.com/subject/1436745/ [2] Full code repository – https://codesandbox.io/s/trusting-engelbart-293wzc?file=/src/index.ts
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.
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.
