Chain of Responsibility
Also known as: CoR, Chain of Command
Purpose
Help the user decide whether Chain of Responsibility is appropriate, then design and apply it to decouple request senders from receivers and replace tangled sequential-check code with a composable, ordered chain of focused handler objects.
When To Use
- User asks to explain the Chain of Responsibility or Chain of Command pattern.
- Code has a growing sequence of checks/validations that have become monolithic and hard to reorder or reuse.
- The set of handlers or their order may change at runtime.
- Multiple handlers may need to process the same request, or exactly one handler should process it — depending on the variant.
- Request handling logic must be reusable across different pipelines without duplication.
- User needs to implement middleware, event bubbling, support escalation, or access-control pipelines.
Inputs
- The request type and its data (what is being passed along the chain).
- The set of handlers needed and their logical order.
- Which CoR variant applies: pass-through (all handlers run) or stop-on-first (first capable handler takes it).
- Whether chain composition must be dynamic (runtime) or static (compile time).
- Language and framework constraints (middleware infrastructure, event systems, etc.).
Workflow
- Clarify the goal. Confirm whether the user wants explanation, code review, or implementation/refactor. Identify the variant: pass-through pipeline vs. stop-on-first handler.
- Check applicability. Confirm requests need sequential processing through independent handlers. If all logic always runs in a fixed order with no interleaving, a simple sequential call chain may suffice.
- Define the Handler interface.
Declare a
handle(request)method. Optionally declaresetNext(handler)if chaining will be explicit. - Create the Base Handler (optional but recommended).
Implement the Handler interface. Hold a
next: Handlerreference. Provide a defaulthandle()that forwards tonextif set. Concrete handlers callsuper.handle()to propagate. - Implement Concrete Handlers. Each handler checks whether it should process the request. In stop-on-first: process and return, or forward. In pass-through: process (possibly augmenting the request), then always forward.
- Compose the chain.
Client (or a factory/configurator) links handlers by calling
setNextor constructor injection. Chain can start from any handler — not necessarily the first. - Trigger the chain. Client sends the request to the entry point. Handle unhandled requests explicitly (log, throw, return a default).
- Validate behavior. Confirm adding a new handler requires no changes to existing handlers or clients. Confirm reordering works by changing only the assembly code.
- Explain tradeoffs. Call out the risk of unhandled requests, debugging difficulty in long chains, and the difference from Decorator.
Decision Branches
- If every handler must always run (pipeline, not early-exit): Use pass-through variant — every handler processes then forwards unconditionally.
- If the first capable handler should claim the request and stop propagation: Use stop-on-first variant — handler either processes or forwards, never both.
- If behaviors must wrap a call and cannot stop it: Use Decorator instead — Decorator cannot stop the call chain; CoR can.
- If a single handler orchestrates communication between many objects: Use Mediator instead.
- If the sender needs a record of the operation for undo/redo: Combine with Command — the request itself becomes a Command object.
- If the chain maps to a Composite tree structure: CoR naturally emerges from the tree — each node forwards to its parent.
Output Contract
When responding, provide:
- A suitability verdict (why CoR or why not) and which variant applies.
- Named roles mapped to the user's domain.
- Step-by-step migration plan from current monolithic check code to handler chain.
- A minimal code sketch in the user's language showing at least two handlers and chain assembly.
- Guidance on handling unhandled requests.
- Validation checklist.
Quality Checks
- Each handler has exactly one responsibility.
- Handlers do not reference each other's concrete types — only the Handler interface.
- Adding a new handler requires zero changes to existing handlers or clients.
- Reordering the chain requires changes only in the assembly / configuration code.
- Unhandled requests are handled explicitly (not silently dropped unless by design).
- In pass-through variant: every handler always forwards; no handler stops the chain.
- In stop-on-first variant: a handler never both processes and forwards the same request.
Common Mistakes To Prevent
- Handlers calling each other directly (bypassing the chain interface) — breaks decoupling.
- Silent request drops when no handler matches and no default end-of-chain behavior exists.
- Mixing pass-through and stop-on-first logic in the same chain without clear documentation.
- Creating a handler that both processes the request AND passes it on in a stop-on-first chain.
- Confusing CoR with Decorator: CoR can stop propagation; Decorator cannot.
- Infinitely long or circular chains with no termination condition.
References
- Detailed guide: REFERENCE.md
- Worked examples: EXAMPLES.md