Loading learning content...
Understanding pattern differences is necessary but insufficient for effective pattern selection. What separates expert practitioners from knowledgeable novices is the ability to systematically navigate from problem characteristics to pattern selection.
Consider the challenge: faced with a design problem, you have eleven behavioral patterns available. Each addresses different aspects of object communication and responsibility distribution. How do you efficiently narrow down to the optimal choice?
The answer lies in decision trees—structured sequences of diagnostic questions that progressively eliminate candidates until the optimal pattern emerges. This approach transforms pattern selection from an art dependent on experience into a systematic process accessible to any engineer.
By the end of this page, you will be able to apply structured decision trees to behavioral pattern selection. You'll learn the diagnostic questions that reveal pattern fit and develop a systematic process for navigating from problem analysis to pattern choice.
The first step in behavioral pattern selection is identifying which category of problem you're solving. Behavioral patterns naturally cluster into five problem categories:
| Problem Category | Key Patterns | Primary Diagnostic Question |
|---|---|---|
| Request Processing & Routing | Chain of Responsibility, Command, Interpreter | How should requests be processed and by whom? |
| State & Behavior Management | State, Memento, Null Object | How should object state affect behavior? |
| Communication & Notification | Observer, Mediator | How should objects communicate changes? |
| Algorithm & Operation Organization | Strategy, Template Method, Visitor | How should algorithms be organized and varied? |
| Traversal & Access | Iterator, Visitor | How should object structures be traversed? |
Some patterns appear in multiple categories (e.g., Visitor in both 'Algorithm Organization' and 'Traversal'). This reflects the multi-faceted nature of some patterns. When a pattern appears in multiple relevant categories, that's often a strong signal it's the right choice.
Initial Category Selection Flowchart:
Start: What is your primary problem?
│
├─▶ "I need to process/route requests" → Request Processing & Routing
│
├─▶ "I need to manage state or behavior changes" → State & Behavior Management
│
├─▶ "I need objects to communicate" → Communication & Notification
│
├─▶ "I need to organize/vary algorithms" → Algorithm & Operation Organization
│
└─▶ "I need to traverse object structures" → Traversal & Access
Once you've identified the problem category, you can apply category-specific decision trees to narrow further.
When your problem involves how requests flow between objects—determining who handles what and how—use this decision tree:
12345678910111213141516171819202122232425262728293031323334
START: Is the request a domain-specific language or expression to evaluate?│├─▶ YES → Is the grammar relatively simple with limited expression types?│ ││ ├─▶ YES → INTERPRETER PATTERN│ │ (Define grammar classes; build expression tree; evaluate recursively)│ ││ └─▶ NO → Consider parser generators or DSL frameworks instead│└─▶ NO → Does the sender need to be decoupled from the receiver? │ ├─▶ YES → Should multiple objects have the opportunity to handle? │ │ │ ├─▶ YES → Should exactly one object handle the request? │ │ │ │ │ ├─▶ YES → CHAIN OF RESPONSIBILITY PATTERN │ │ │ (Chain handlers; each decides to handle or pass) │ │ │ │ │ └─▶ NO (multiple handlers) → Consider OBSERVER │ │ or combine CHAIN OF RESPONSIBILITY with broadcasting │ │ │ └─▶ NO (single known receiver) → COMMAND PATTERN │ (Encapsulate request; decouple invoker from receiver) │ └─▶ NO → Do you need to: │ - Queue, log, or schedule requests? │ - Support undo/redo? │ - Parameterize actions as objects? │ ├─▶ YES to any → COMMAND PATTERN │ (Reify actions; store, queue, or undo them) │ └─▶ NO → Direct method calls may suffice (Consider if a pattern is truly needed)The Interpreter pattern is powerful but narrow in applicability. It's ideal for simple, specialized DSLs (like regex engines or rule systems). For complex languages, dedicated parser generators (ANTLR, Bison) are more appropriate than hand-rolled interpreters.
When your problem involves how object state affects behavior—including state transitions, state snapshots, and handling missing objects—use this decision tree:
1234567891011121314151617181920212223242526272829303132333435363738
START: Does your object need to change behavior based on internal state?│├─▶ YES → Are there discrete states with distinct behaviors?│ ││ ├─▶ YES → Is there complex conditional logic (switch/if-else) based on state?│ │ ││ │ ├─▶ YES → STATE PATTERN│ │ │ (Encapsulate each state as a class; delegate behavior to current state)│ │ ││ │ └─▶ NO → Consider simple conditionals if states are few and simple│ ││ └─▶ NO → Is the behavior change about choosing algorithms?│ ││ ├─▶ YES → STRATEGY PATTERN│ │ (See Algorithm Organization decision tree)│ ││ └─▶ NO → Consider if behavior really varies or if logic is elsewhere│├─▶ NO → Do you need to capture and restore object state?│ ││ ├─▶ YES → Should restoration NOT break encapsulation?│ │ ││ │ ├─▶ YES → MEMENTO PATTERN│ │ │ (Externalize state in memento; restore without exposing internals)│ │ ││ │ └─▶ NO → Consider serialization or data transfer objects│ ││ └─▶ NO → Do you need to handle absence of objects gracefully?│ ││ ├─▶ YES → Are null checks scattered throughout code?│ │ ││ │ ├─▶ YES → NULL OBJECT PATTERN│ │ │ (Provide do-nothing implementation of expected interface)│ │ ││ │ └─▶ NO → Consider Optional types or Maybe monads│ ││ └─▶ NO → State/behavior management patterns may not apply│ (Re-evaluate problem category)Look for:
Look for:
When your problem involves how objects communicate state changes or coordinate activities, use this decision tree:
1234567891011121314151617181920212223242526272829303132
START: How many objects need to communicate?│├─▶ ONE-TO-MANY (broadcast)│ ││ └─▶ Should dependents be notified automatically when state changes?│ ││ ├─▶ YES → OBSERVER PATTERN│ │ ││ │ └─▶ Further: Push or Pull notification model?│ │ ├─▶ Push: Subject sends state data in notification│ │ └─▶ Pull: Subject sends notification; observers query state│ ││ └─▶ NO → Consider event-driven architecture or messaging systems│├─▶ MANY-TO-MANY (complex interactions)│ ││ └─▶ Are objects becoming tightly coupled with direct references?│ ││ ├─▶ YES → MEDIATOR PATTERN│ │ (Centralize communication; colleagues interact via mediator only)│ ││ └─▶ NO → Direct communication may be acceptable│ (Monitor coupling; refactor to Mediator if it grows)│└─▶ ONE-TO-ONE (targeted) │ └─▶ Is the receiver identity dynamic or needs decoupling? │ ├─▶ YES → CHAIN OF RESPONSIBILITY (receiver selection) │ or COMMAND (encapsulated request) │ └─▶ NO → Direct method call likely sufficientWhile Mediator reduces coupling between colleagues, it concentrates logic in the mediator itself. If your mediator grows into a massive class with intimate knowledge of all colleagues, you've traded one form of coupling for another. Consider decomposing into multiple specialized mediators or using Observer for simpler notifications.
When your problem involves how algorithms are structured, varied, or applied to object structures, use this decision tree:
1234567891011121314151617181920212223242526272829303132333435363738394041424344
START: What aspect of algorithms needs to vary?│├─▶ ENTIRE ALGORITHM is replaceable│ ││ └─▶ Should algorithm be selected at runtime?│ ││ ├─▶ YES → Do multiple objects need the same algorithm?│ │ ││ │ ├─▶ YES → STRATEGY PATTERN (shared strategies)│ │ │ (Inject strategy; multiple contexts can share)│ │ ││ │ └─▶ NO → STRATEGY PATTERN (per-object strategy)│ │ (Each context has its own strategy reference)│ ││ └─▶ NO (compile-time) → Consider Template Method or simple inheritance│├─▶ ALGORITHM STRUCTURE fixed, but STEPS vary│ ││ └─▶ Is the algorithm skeleton defined in a base class?│ ││ ├─▶ YES → Can subclasses override specific steps?│ │ ││ │ ├─▶ YES → TEMPLATE METHOD PATTERN│ │ │ (Abstract class defines skeleton; hook methods for customization)│ │ ││ │ └─▶ NO → Consider Strategy for step injection│ ││ └─▶ NO → STRATEGY PATTERN may be more appropriate│ (Composition over inheritance)│└─▶ OPERATIONS over object structures │ └─▶ Do you need to add operations without modifying element classes? │ ├─▶ YES → Are element types stable (rarely adding new types)? │ │ │ ├─▶ YES → VISITOR PATTERN │ │ (Double dispatch; elements accept visitors) │ │ │ └─▶ NO → Visitor becomes burdensome │ (Consider type switches or Pattern Matching) │ └─▶ NO → Adding operations to classes directly may be fine (Only use Visitor if modification is truly prohibited)Choose Strategy when:
Choose Template Method when:
Visitor excels when:
Avoid Visitor when:
When your problem involves traversing or accessing elements in object structures, use this decision tree:
12345678910111213141516171819202122232425262728
START: What do you need to do during traversal?│├─▶ ACCESS ELEMENTS sequentially without exposing structure│ ││ └─▶ ITERATOR PATTERN│ ││ └─▶ Further: Internal or External iterator?│ ├─▶ External: Client controls iteration (next(), hasNext())│ └─▶ Internal: Collection controls iteration (forEach with callback)│├─▶ PERFORM OPERATIONS on elements during traversal│ ││ └─▶ Should operations be separated from element classes?│ ││ ├─▶ YES → VISITOR PATTERN│ │ (Combine with Iterator for traversal; Visitor for operations)│ ││ └─▶ NO → Consider methods on elements or Iterator with callbacks│└─▶ TRAVERSE SPECIFIC STRUCTURES (trees, graphs, composites) │ └─▶ Is the structure a Composite pattern? │ ├─▶ YES → ITERATOR over composite hierarchy │ (Consider depth-first vs breadth-first) │ └─▶ NO → Standard Iterator implementations (Array, list, tree, graph traversals)Iterator and Visitor are complementary patterns. Iterator handles HOW to traverse (the order of element access), while Visitor handles WHAT to do (the operation applied). Complex object structure processing often uses both: Iterator provides traversal; Visitor provides operation dispatch.
For rapid pattern selection, here's a consolidated flowchart that guides you from problem characteristics to pattern recommendations:
12345678910111213141516171819202122232425262728293031
┌─────────────────────────────────────────────────────────────────┐│ BEHAVIORAL PATTERN SELECTOR │├─────────────────────────────────────────────────────────────────┤│ ││ ▶ Need to DECOUPLE SENDER from RECEIVER? ││ ├─ Multiple handlers should try? → CHAIN OF RESPONSIBILITY ││ ├─ Need undo/queue/log? → COMMAND ││ └─ Broadcast to many? → OBSERVER ││ ││ ▶ Need to MANAGE OBJECT STATE? ││ ├─ Behavior changes with state? → STATE ││ ├─ Need state snapshots? → MEMENTO ││ └─ Handle absence gracefully? → NULL OBJECT ││ ││ ▶ Need to COORDINATE COMMUNICATION? ││ ├─ One-to-many notification? → OBSERVER ││ └─ Many-to-many coordination? → MEDIATOR ││ ││ ▶ Need to VARY ALGORITHMS? ││ ├─ Entire algorithm swappable? → STRATEGY ││ ├─ Fixed structure, varying steps? → TEMPLATE METHOD ││ └─ Operations over structures? → VISITOR ││ ││ ▶ Need to TRAVERSE STRUCTURES? ││ ├─ Sequential access? → ITERATOR ││ └─ Apply operations during traversal? → VISITOR + ITERATOR ││ ││ ▶ Need to EVALUATE DSL/EXPRESSIONS? ││ └─ Simple grammar representation? → INTERPRETER ││ │└─────────────────────────────────────────────────────────────────┘| Problem Characteristic | Recommended Pattern | Key Benefit |
|---|---|---|
| Sender doesn't know receiver | Chain of Responsibility | Complete decoupling |
| Need to undo operations | Command + Memento | History/rollback |
| State causes behavior change | State | Eliminates conditionals |
| Need to notify observers | Observer | Loose coupling |
| Complex inter-object coordination | Mediator | Centralized control |
| Runtime algorithm selection | Strategy | Flexibility |
| Fixed algorithm, varying steps | Template Method | Code reuse |
| Add operations without modification | Visitor | Open/Closed compliance |
| Uniform collection access | Iterator | Encapsulation |
| Handle nulls polymorphically | Null Object | Cleaner code |
| Implement simple DSL | Interpreter | Expressiveness |
Let's apply the decision tree to a real design problem to see how systematic selection works in practice.
Scenario: Document Processing Pipeline
You're designing a document processing system where:
Step 1: Identify Problem Category
Multiple aspects are at play:
The PRIMARY concern is request processing—how documents flow through stages.
Step 2: Apply Request Processing Decision Tree
"Is this a DSL?" → NO
"Does sender need decoupling from receiver?" → YES (documents don't know their handlers)
"Should multiple objects have opportunity to handle?" → YES (multiple stages)
"Should exactly one object handle?" → NO (multiple stages process)
This suggests Chain of Responsibility for the core flow, but the fact that multiple handlers process (not just one) indicates we might need a variant.
Step 3: Consider Hybrid Requirements
Step 4: Final Recommendation
Primary Pattern: Chain of Responsibility (pipeline variant where each handler processes AND passes) Supporting Pattern: Command (encapsulate each stage operation for undo/logging)
This combination gives you:
Real-world solutions often combine multiple patterns. The decision tree helps identify the primary pattern, but analyzing secondary requirements frequently reveals supporting patterns. The next page explores pattern combinations in depth.
We've transformed behavioral pattern selection from intuition-based guessing into a systematic, question-driven process. Here are the key insights:
What's Next:
The decision trees help select individual patterns, but real-world architectures often require pattern combinations. The next page explores how behavioral patterns complement each other and techniques for combining patterns effectively.
You now have a systematic framework for behavioral pattern selection. The decision trees transform pattern knowledge into actionable selection criteria. Practice applying these trees to design problems to build selection intuition.