CHAPTER 16
Intermediate
State and Chain of Responsibility Patterns
Updated: May 16, 2026
35 min read
# CHAPTER 16
State and Chain of Responsibility Patterns
1. Introduction
In complex enterprise systems, a request rarely executes a single, simple function. A user's order must transition through multiple physical statuses (Pending -> Paid -> Shipped). Similarly, an incoming HTTP request must pass through a gauntlet of security checks (Authentication -> Authorization -> Rate Limiting) before it touches the database. If you hardcode these workflows using massiveif/else statements, your code will become an unmaintainable, fragile maze. In this chapter, we will master the State and Chain of Responsibility patterns. We will learn how to build Finite State Machines to manage complex object lifecycles, and deploy Middleware pipelines to sequentially process incoming requests.
2. Learning Objectives
By the end of this chapter, you will be able to:- Define the intent and mechanics of the State Pattern.
-
Eliminate "Finite State Machine"
switchstatements using polymorphism.
- Define the intent of the Chain of Responsibility (CoR) Pattern.
- Architect a decoupled, sequential request-processing pipeline.
- Identify the differences between State management and Chain routing.
3. The State Pattern (The Finite State Machine)
The State pattern allows an object to alter its behavior when its internal state changes. It appears as if the object changed its class.-
The Problem: A
Documentobject can be inDraft,Review, orPublishedstate. If a user clicks "Publish", the logic depends entirely on the current state. If you handle this with a giantswitch ($this->state)block inside every single method, the code rots.
-
The Solution: Extract each state into its own class (
DraftState,PublishedState). TheDocument(Context) holds a reference to one of these state objects. When the user clicks "Publish", theDocumentsimply delegates the action to its current State object.
4. The Chain of Responsibility (The Pipeline)
The Chain of Responsibility passes a request along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.- The Problem: A user submits a form. You need to 1) Authenticate the session, 2) Validate the data, 3) Check for spam. If you hardcode these 3 checks sequentially in the controller, they become tightly coupled.
-
The Solution: Create 3 independent "Handler" objects. Link them together like a linked list (
Authpoints toValidationpoints toSpam). You hand the request to the first Handler. If it passes Auth, the Auth handler hands it to Validation. If a handler fails, it instantly rejects the request, breaking the chain.
5. State vs. Strategy
Structurally, State and Strategy are nearly identical (a Context delegating to an Interface).- Strategy: The Context relies on the client to give it a Strategy (e.g., The user clicks "Bike Route"). Strategies *do not know* about each other.
-
State: The States actively transition *themselves* (e.g., The
DraftStatefinishes review and automatically swaps the Context over to thePublishedState). States *know* about each other.
6. UML Diagrams
*Chain of Responsibility Structure*
text
7. Code Example: Chain of Responsibility (PHP)
Let's build a server Middleware pipeline.
php
8. Best Practices
- The "Catch-All" Fallback: In a Chain of Responsibility, what happens if a request reaches the very end of the chain and *no* handler processed it? Depending on the architecture, it might silently disappear. Always ensure your chain has a default "Catch-All" handler at the very end to log unhandled requests or throw an explicit Exception.
9. Common Mistakes
-
State Pattern Overhead: A junior developer implements the State pattern for an
Orderobject, creating 15 different State classes. *The Failure:* If an Order state only determines a string value in the database and doesn't drastically change the *behavior* of the methods, the State pattern is massive overkill. A simpleenumor string field is sufficient. Only use the State pattern when the *logic* of the object's methods completely changes based on its status.
10. Mini Project: Build an Order Workflow (State)
-
1.
Context: Create an
Orderclass.
-
2.
State Interface: Create a
Stateinterface withpay(),ship(), andcancel().
-
3.
States: Create
PendingState,PaidState,ShippedState.
-
4.
Logic: In
PendingState,pay()works and transitions the context toPaidState. InShippedState, callingcancel()throws an error ("Cannot cancel shipped order").
-
5.
Action: Test the workflow. Notice how the
Orderclass has noif/elsestatements.
11. Practice Exercises
- 1. Explain how the Chain of Responsibility pattern is heavily utilized in modern web frameworks (like Laravel or Express.js) under the term "Middleware."
- 2. Define the architectural difference between the Strategy pattern and the State pattern regarding how they transition between their internal objects.
12. MCQs with Answers
Question 1
An architect is building an HTTP request pipeline. The incoming request must be passed through a rate limiter, an authenticator, and a data sanitizer. If any of these modules fail, the request must be instantly rejected. Which pattern allows the architect to link these modules sequentially without tightly coupling them?
Question 2
When a VendingMachine object has 0 inventory, pressing the "Buy" button does nothing. When it has inventory, pressing "Buy" dispenses a soda. To avoid writing a massive switch statement based on the inventoryCount variable, the architect encapsulates these specific behaviors into EmptyState and ReadyState classes. Which pattern is this?
13. Interview Questions
- Q: In a Chain of Responsibility, walk me through the memory implications of creating a very long linked list of Handlers. Can this pattern cause Stack Overflow exceptions if poorly implemented?
-
Q: Explain how the State pattern resolves the architectural "Code Smell" of a massive Finite State Machine
switchblock. How does it leverage polymorphism?
- Q: Compare the Chain of Responsibility pattern with the Decorator pattern. Structurally, they both involve objects containing references to other objects of the same type. How do their behavioral intents differ?
14. FAQs
Q: Can a handler in the Chain modify the request before passing it on? A: Absolutely. This is the exact definition of Middleware. The Authentication handler might decode a JWT token, extract the User ID, attach that ID to the Request object, and pass the enriched request to the next handler.15. Summary
In Chapter 16, we tamed the complexity of workflows and pipelines. We recognized that massiveswitch statements used to manage object lifecycles are fragile anti-patterns, and deployed the State Pattern to distribute this logic into autonomous, polymorphic state classes. We abandoned tightly coupled procedural checks in favor of the Chain of Responsibility, engineering elegant, sequential middleware pipelines capable of authenticating, validating, and routing requests dynamically. These patterns allow us to map complex business rules into clean, decoupled software architecture.