Master Dynamic Behavior with the State Pattern
In this lesson, you will learn how to make an object change its behavior dynamically when its internal state shifts, without cluttering your code with endless if-else blocks.
The Problem: The "Switch" Nightmare
Imagine you are coding a Document class. If the state is Draft, calling Publish() might trigger a save-to-disk operation. If the state is Published, calling Publish() might throw an error or do nothing. If you use a simple if (state == "Draft") check inside every method, your code becomes a fragile mess as you add more states like Archived or Review.
The Solution: State Objects
Instead of the object checking its state, we delegate the work to State objects. Each state becomes its own class that implements a common interface.
interface IDocumentState {
void Publish(Document doc);
}
class DraftState : IDocumentState {
public void Publish(Document doc) {
Console.WriteLine("Publishing the draft...");
doc.State = new PublishedState(); // Transition to next state
}
}
class PublishedState : IDocumentState {
public void Publish(Document doc) => Console.WriteLine("Already published!");
}
class Document {
public IDocumentState State { get; set; } = new DraftState();
public void Publish() => State.Publish(this);
}
How It Works
- Encapsulation: The behavior for a specific state is locked away inside its own class.
- Delegation: The
Documentclass doesn't know how to publish; it just asks theStateobject to handle it. - Transition: When a method is called, the current state object can swap itself out for a new one (as seen in
DraftState), effectively changing the object's identity and behavior for the next time a method is called.
This approach keeps your Document class clean and makes it incredibly easy to add new states later—you simply create a new class rather than modifying existing logic.
Key Takeaway: The State Pattern replaces conditional logic with polymorphic state objects, allowing an object to alter its behavior by swapping its internal state handler.