Module 3: Behavioral Patterns: Case Studies
Behavioral patterns are useful when they make changing behavior explicit. These cases emphasize the decision pressure that makes a pattern worth using.
Case Study 1: Pricing Rules as Strategy
Scenario: A SaaS billing system supports monthly, annual, nonprofit, enterprise, and promotional pricing. A single calculatePrice(plan, customer, coupon) function has nested conditionals and special cases for renewal dates.
Source anchor: Design Patterns Catalog - Refactoring.Guru.
Module concepts:
- Strategy
- open/closed principle
- policy objects
- testable behavior
Wrong Approach
Keep adding if statements because all pricing decisions are in one visible place.
Better Approach
Introduce a PricingStrategy for each pricing family and a factory or registry that selects the strategy from plan and customer context. Keep shared rounding and currency behavior outside individual strategies so each class owns one pricing decision.
Tradeoff Table
| Choice | Gain | Cost |
|---|---|---|
| One function | Easy to inspect initially | Becomes brittle as rules grow |
| Strategy classes | Isolated rule changes | More files and selection logic |
| Data-driven rules | Business-friendly updates | Needs validation and rule tooling |
Failure Mode
A promotion fix for nonprofit customers accidentally changes enterprise renewal pricing.
Required Artifact
Build a strategy selection table with inputs, selected strategy, owned rule, shared dependency, and test cases.
Project / Capstone Connection
Use this table before replacing a conditional with a pattern in the semester project.
Case Study 2: Observer for Domain Events Without Leaks
Scenario: An order service must update analytics, send email, and notify fulfillment after an order is paid. The first implementation calls all three systems directly from payOrder().
Source anchor: Design Patterns Catalog - Refactoring.Guru.
Module concepts:
- Observer
- domain events
- coupling control
- lifecycle and unsubscribe risks
Wrong Approach
Make every subsystem observe every order event and let observers call back into the order service freely.
Better Approach
Publish a clear OrderPaid event with stable data, register observers at application boundaries, and make handlers idempotent. Avoid hidden synchronous dependency cycles; decide explicitly whether event handling is in-process, queued, or transactional.
Tradeoff Table
| Choice | Gain | Cost |
|---|---|---|
| Direct calls | Simple tracing | Tight coupling |
| In-process observers | Easy extension | Ordering and failure semantics matter |
| Queue-backed events | Better isolation | Operational complexity |
Failure Mode
An email observer throws an exception and rolls back payment because event failure semantics were never designed.
Required Artifact
Create an event contract with payload, delivery timing, retry behavior, idempotency key, and observer ownership.
Project / Capstone Connection
Use this contract for any project workflow that fans out after a state change.
Case Study 3: Command for Undoable Editor Actions
Scenario: A drawing app needs undo and redo for move, resize, recolor, delete, and group operations. UI handlers directly mutate canvas objects, so undo code duplicates pieces of each action.
Source anchor: Design Patterns Catalog - Refactoring.Guru.
Module concepts:
- Command
- undo and redo
- action history
- encapsulated request
Wrong Approach
Store snapshots after every UI event and hope memory usage stays acceptable.
Better Approach
Represent each user action as a command with execute() and undo() behavior, store enough state to reverse the command, and make compound commands explicit for grouped edits.
Tradeoff Table
| Choice | Gain | Cost |
|---|---|---|
| Full snapshots | Simple undo model | High memory use |
| Command objects | Precise history | Must capture inverse state correctly |
| Event sourcing | Rich replay | Larger architecture decision |
Failure Mode
Undo restores position but not color because the action's inverse state was incomplete.
Required Artifact
Write command contracts for three editor actions, including execute inputs, captured undo state, redo behavior, and failure handling.
Project / Capstone Connection
Use this for any capstone feature that records user intent or deferred work.
Case Study 4: State Pattern for Order Lifecycle
Scenario: Orders move through draft, placed, paid, shipped, delivered, returned, and cancelled states. Methods contain checks like if status == "shipped" across controllers, jobs, and support tools.
Source anchor: Design Patterns Catalog - Refactoring.Guru.
Module concepts:
- State
- finite state machines
- transition rules
- illegal operation handling
Wrong Approach
Create a bigger enum and add more status checks wherever a feature needs them.
Better Approach
Model allowed transitions explicitly. Use state objects or a transition table when behavior changes by state, and centralize illegal transition errors so callers cannot invent their own lifecycle rules.
Tradeoff Table
| Choice | Gain | Cost |
|---|---|---|
| Scattered status checks | Fast local edits | Inconsistent lifecycle rules |
| Transition table | Clear allowed movement | Less natural for rich behavior |
| State objects | Behavior near state | More classes |
Failure Mode
Support cancels an already shipped order because their tool had an outdated status check.
Required Artifact
Create a state transition diagram with allowed commands, forbidden commands, side effects, and tests.
Project / Capstone Connection
Use this diagram before implementing workflow states in the project.
Case Study 5: Chain of Responsibility for Approval Rules
Scenario: Expense reports require manager approval, finance approval, security approval for hardware, and executive approval above a threshold. The first implementation hard-codes the approval order in one function.
Source anchor: Design Patterns Catalog - Refactoring.Guru.
Module concepts:
- Chain of Responsibility
- handler order
- short-circuit behavior
- policy composition
Wrong Approach
Keep one function with nested conditionals and add comments explaining the order.
Better Approach
Represent each approval rule as a handler with a clear input, output, and stop/continue decision. Test the chain order and make ownership explicit because policy order is part of behavior.
Tradeoff Table
| Choice | Gain | Cost |
|---|---|---|
| One approval function | All rules visible | Hard to extend safely |
| Handler chain | Modular rules | Order must be governed |
| Rule engine | Powerful policies | Can exceed module complexity |
Failure Mode
Executive approval runs before finance validation and approves an invalid expense.
Required Artifact
Produce a handler sequence table with preconditions, decision, continuation behavior, owner, and tests.
Project / Capstone Connection
Use this artifact when modeling validation or authorization pipelines.
Source Map
| Source | Use it for |
|---|---|
| Design Patterns Catalog - Refactoring.Guru | Pattern vocabulary, intent, and comparison across behavioral patterns. |