Skip to main content

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

ChoiceGainCost
One functionEasy to inspect initiallyBecomes brittle as rules grow
Strategy classesIsolated rule changesMore files and selection logic
Data-driven rulesBusiness-friendly updatesNeeds 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

ChoiceGainCost
Direct callsSimple tracingTight coupling
In-process observersEasy extensionOrdering and failure semantics matter
Queue-backed eventsBetter isolationOperational 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

ChoiceGainCost
Full snapshotsSimple undo modelHigh memory use
Command objectsPrecise historyMust capture inverse state correctly
Event sourcingRich replayLarger 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

ChoiceGainCost
Scattered status checksFast local editsInconsistent lifecycle rules
Transition tableClear allowed movementLess natural for rich behavior
State objectsBehavior near stateMore 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

ChoiceGainCost
One approval functionAll rules visibleHard to extend safely
Handler chainModular rulesOrder must be governed
Rule enginePowerful policiesCan 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

SourceUse it for
Design Patterns Catalog - Refactoring.GuruPattern vocabulary, intent, and comparison across behavioral patterns.