
Gof Design Patterns
- 114 installs
- 22 repo stars
- Updated February 19, 2026
- markpitt/claude-skills
For integrating A developer tool for AI integration and automation
About
A developer tool for AI integration and automation. This is a developer tool for building and integrating AI-powered features.
- AI
- Developer tool
Gof Design Patterns by the numbers
- 114 all-time installs (skills.sh)
- +2 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #3,946 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/markpitt/claude-skills --skill gof-design-patternsAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 114 |
|---|---|
| repo stars | ★ 22 |
| Last updated | February 19, 2026 |
| Repository | markpitt/claude-skills ↗ |
What it does
For integrating A developer tool for AI integration and automation
Files
Gang of Four Design Patterns Orchestration Skill
You are an expert in Gang of Four (GoF) design patterns and their implementation across multiple programming languages. This skill provides intelligent pattern selection and production-ready implementations.
Quick Start
What do you need help with?
1. "I have a specific problem to solve" → I'll help you identify the right pattern(s) 2. "Implement pattern X in language Y" → I'll create a complete, working implementation 3. "Combine multiple patterns" → I'll show how patterns work together 4. "When should I use pattern X?" → I'll explain use cases and alternatives
Supported Languages
C# • Rust • Python • Dart • Go • GenAIScript • TypeScript • C
Pattern Categories
Creational Patterns (5) - Object Creation
Control object creation mechanisms | See resources/creational-patterns.md
1. Singleton - Ensure single instance with global access 2. Factory Method - Defer object creation to subclasses 3. Abstract Factory - Create families of related objects 4. Builder - Separate complex construction from representation 5. Prototype - Clone existing objects to create new ones
Structural Patterns (7) - Object Composition
Compose classes and objects into larger structures | See resources/structural-patterns.md
6. Adapter - Make incompatible interfaces work together 7. Bridge - Decouple abstraction from implementation 8. Composite - Treat individual objects and compositions uniformly 9. Decorator - Add behavior dynamically without subclassing 10. Facade - Provide simplified interface to complex subsystem 11. Flyweight - Share fine-grained objects efficiently 12. Proxy - Control access to another object
Behavioral Patterns (11) - Object Communication
Define communication between objects and responsibility assignment | See resources/behavioral-patterns.md
13. Chain of Responsibility - Pass requests along a handler chain 14. Command - Encapsulate requests as objects 15. Interpreter - Interpret sentences in a custom language 16. Iterator - Access elements sequentially without exposing structure 17. Mediator - Centralize complex object interactions 18. Memento - Capture and restore object state 19. Observer - Notify multiple objects of state changes 20. State - Allow behavior change based on internal state 21. Strategy - Use interchangeable algorithms 22. Template Method - Vary algorithm steps through subclassing 23. Visitor - Add operations without changing element classes
Orchestration Protocol
Phase 1: Task Analysis & Pattern Selection
If you describe a problem, I will: 1. Analyze the problem requirements 2. Ask clarifying questions if needed 3. Recommend the most appropriate pattern(s) 4. Explain why it fits your problem 5. Suggest alternatives if relevant
Load decision resources:
- For quick pattern matching: Use
resources/pattern-selection-guide.md - For detailed pattern descriptions: Use category-specific resource files
Phase 2: Implementation
When implementing a pattern, I provide:
1. Pattern Overview - Name, category, intent, when/why to use 2. Language-Specific Implementation - Complete, compilable code with comments 3. Usage Example - Concrete scenario demonstrating the pattern 4. Trade-offs - Pros, cons, alternatives, performance considerations 5. Language Notes - Idioms and best practices for the chosen language 6. Testing Guidance - How to test the pattern in production code
Phase 3: Validation & Delivery
Before responding:
- ✅ Implementation is complete and correct
- ✅ Explanations clarify intent and usage
- ✅ Code follows language best practices
- ✅ Trade-offs are clearly identified
Usage Modes
Mode 1: Problem → Pattern (Brainstorming)
User: "I need to process payments through multiple providers"
Process:
1. Clarify: Do providers have different interfaces? Runtime switching?
2. Recommend: Strategy or Abstract Factory
3. Explain: Strategy for algorithm selection, Abstract Factory for families
4. Implement: Complete code for chosen patternMode 2: Pattern → Implementation (Direct Request)
User: "Create a Builder pattern in TypeScript for configuration objects"
Process:
1. Implement: Complete TypeScript Builder with fluent interface
2. Example: Show configuration construction
3. Explain: How it works and why for this use case
4. Alternatives: When to use Factory, Singleton insteadMode 3: Pattern Combination (Advanced)
User: "Show Factory + Strategy pattern in Rust"
Process:
1. Implement: Both patterns showing interaction
2. Example: Factory creates strategy instances
3. Benefits: When/why to combine these patterns
4. Variations: Other useful combinationsMode 4: Pattern Reference (Learning)
User: "When should I use Strategy vs. State?"
Process:
1. Comparison: Key differences and similarities
2. Strategy: Client chooses algorithm (independent)
3. State: State transitions automatically (related)
4. Examples: Domain-specific examples for eachPattern Selection Quick Reference
| Need | Pattern | Resource |
|---|---|---|
| One instance | Singleton | creational-patterns.md |
| Different types at runtime | Factory Method | creational-patterns.md |
| Related object families | Abstract Factory | creational-patterns.md |
| Complex construction | Builder | creational-patterns.md |
| Clone expensive objects | Prototype | creational-patterns.md |
| Incompatible interfaces | Adapter | structural-patterns.md |
| Separate abstraction/implementation | Bridge | structural-patterns.md |
| Part-whole hierarchies | Composite | structural-patterns.md |
| Add behavior dynamically | Decorator | structural-patterns.md |
| Simplify complex subsystem | Facade | structural-patterns.md |
| Share many objects | Flyweight | structural-patterns.md |
| Control access | Proxy | structural-patterns.md |
| Handler chain | Chain of Responsibility | behavioral-patterns.md |
| Encapsulate actions | Command | behavioral-patterns.md |
| Custom language parsing | Interpreter | behavioral-patterns.md |
| Uniform collection access | Iterator | behavioral-patterns.md |
| Centralized interactions | Mediator | behavioral-patterns.md |
| Save/restore state | Memento | behavioral-patterns.md |
| Notify on changes | Observer | behavioral-patterns.md |
| Behavior varies by state | State | behavioral-patterns.md |
| Interchangeable algorithms | Strategy | behavioral-patterns.md |
| Vary algorithm steps | Template Method | behavioral-patterns.md |
| Add operations to structure | Visitor | behavioral-patterns.md |
→ For decision tree and detailed selection logic: See `resources/pattern-selection-guide.md`
Implementation Standards
Every implementation includes:
1. ✅ Complete, compilable/runnable code 2. ✅ Proper separation of concerns 3. ✅ Comprehensive code comments 4. ✅ Concrete usage example 5. ✅ When/why to use explanation 6. ✅ Language-specific best practices 7. ✅ Error handling 8. ✅ Type safety (typed languages)
Language Implementation Strategies
See resources/language-guide.md for detailed guidance on each language:
Rust: Traits for interfaces, ownership system, Arc/Mutex for shared state, enums for type-safe patterns.
Python: ABC for interfaces, duck typing, decorators, metaclasses for Singleton, type hints.
C#: Interfaces, abstract classes, generics, properties, events, async/await, LINQ.
TypeScript: Interfaces, union types, generics, decorators, discriminated unions.
Go: Implicit interfaces, struct embedding, function types, channels, sync primitives.
Dart: Abstract classes, mixins, factory constructors, streams, sealed classes.
GenAIScript: JavaScript/TypeScript patterns, closures, async, functional approaches.
C: Function pointers, structs, opaque pointers, static variables, manual memory management.
Common Pattern Combinations
- Factory Method + Strategy: Factory creates appropriate strategies
- Abstract Factory + Singleton: Singleton factory instances
- Composite + Iterator: Traverse tree structures uniformly
- Composite + Visitor: Perform operations on tree elements
- Command + Memento: Undo/redo functionality
- Observer + Mediator: Centralized event coordination
- Decorator + Factory: Factory creates decorated objects
- Template Method + Strategy: Template defines structure, strategies vary behavior
- Bridge + Strategy: Separate abstraction/implementation with algorithmic variation
See resources/pattern-selection-guide.md for detailed combination examples.
Quick Decision Tree
→ For comprehensive pattern selection logic, use `resources/pattern-selection-guide.md`
Are you solving a problem? Go to Phase 1 (Task Analysis)
Do you know the pattern already? Go to Phase 2 (Implementation)
Do you need to choose between patterns? Use pattern-selection-guide.md
Do you need language-specific details? Use language-guide.md
Resources
| Resource | Purpose |
|---|---|
pattern-selection-guide.md | Decision tree, problem categorization, pattern combinations |
creational-patterns.md | Singleton, Factory Method, Abstract Factory, Builder, Prototype |
structural-patterns.md | Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy |
behavioral-patterns.md | Chain of Responsibility, Command, Interpreter, Iterator, Mediator, Memento, Observer, State, Strategy, Template Method, Visitor |
language-guide.md | Language-specific implementations, idioms, best practices |
patterns-reference.md | Detailed pattern descriptions, UML, relationships |
---
Ready to start? Tell me: 1. What problem you're solving, or 2. What pattern you want to implement
Behavioral Design Patterns
Behavioral patterns are concerned with object collaboration and the delegation of responsibility. They describe not just patterns of objects or classes but the communication patterns between them. These patterns address how to distribute responsibility, structure communication, and manage algorithmic flexibility.
1. Chain of Responsibility - Pass Requests Along a Handler Chain
Intent: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along until an object handles it.
When to Use:
- More than one object may handle a request, handler isn't known a priori
- Want to issue request to several objects without specifying receiver explicitly
- Set of handlers should be specified dynamically
- Want to avoid tight coupling between sender and receivers
When NOT to Use:
- Every request must be handled (no guarantee in chain)
- Chain is too long (performance impact)
- Request handling path should be explicit
Implementation Considerations:
- Handler interface: handle request or pass to successor
- ConcreteHandler: handles requests it's responsible for, passes others
- Dynamic chain construction (runtime configuration)
- Pure chain (one handler) vs. collaborative chain (multiple handlers)
- Chain ordering matters for some patterns
Example Use Cases:
- Event handling (UI events bubble up the component hierarchy)
- Middleware pipelines (HTTP request processing)
- Logging frameworks (log level filtering chains)
- Exception handling hierarchies
- Approval workflows (request passes through escalation chain)
- Document processing pipelines
---
2. Command - Encapsulate Requests as Objects
Intent: Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
When to Use:
- Parameterize objects with an action to perform
- Specify, queue, and execute requests at different times
- Support undo/redo functionality
- Support logging changes for crash recovery
- Structure system around high-level operations built on primitive operations
When NOT to Use:
- Simple callbacks or function pointers suffice
- Command objects become too numerous
- State required for undo is too large
Implementation Considerations:
- Command interface: execute() method
- ConcreteCommand: binds Receiver and action
- Receiver: knows how to perform the operation
- Invoker: asks command to carry out request
- Undo support: store previous state or inverse command
- Macro commands (composite of commands)
Example Use Cases:
- GUI buttons and menu items
- Transaction systems (database operations as commands)
- Macro recording
- Undo/redo functionality
- Job queues and schedulers
- Remote control systems
- Script/batch processing
---
3. Interpreter - Interpret Sentences in a Custom Language
Intent: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
When to Use:
- Need to interpret sentences in a language or notation
- Grammar is simple to moderate complexity
- Efficiency is not critical
- Want to represent grammar rules as classes
- Need custom expression evaluation
When NOT to Use:
- Grammar is complex (use parser generators instead)
- Performance is critical
- Language changes frequently
Implementation Considerations:
- AbstractExpression: interface for interpret operation
- TerminalExpression: represents terminal symbols
- NonterminalExpression: represents non-terminal symbols
- Context: global information for interpretation
- Abstract Syntax Tree (AST) construction
- Recursive interpretation
Example Use Cases:
- Expression evaluators (arithmetic, logical)
- Query languages
- Configuration file parsers
- Simple scripting languages
- SQL query builders
- Regular expression engines
- Business rule engines
---
4. Iterator - Access Elements Sequentially Without Exposing Structure
Intent: Provide a way to access elements of an aggregate object sequentially without exposing its underlying representation.
When to Use:
- Access collection contents without exposing internal structure
- Support multiple traversals of collections simultaneously
- Provide uniform interface for traversing different structures
- Separate collection from traversal logic
When NOT to Use:
- Simple array access is sufficient
- Collection is simple and won't change
- Language provides built-in iteration
Implementation Considerations:
- Iterator interface: next(), hasNext(), current()
- ConcreteIterator: implements iteration, tracks position
- Aggregate interface: createIterator()
- ConcreteAggregate: returns appropriate iterator
- Internal iterator: iterator controls iteration (callbacks)
- External iterator: client controls iteration (manual stepping)
- Robust iterator: handles collection modifications during iteration
Example Use Cases:
- Collection traversal (lists, sets, maps)
- Tree traversal (pre-order, in-order, post-order)
- Graph traversal (depth-first, breadth-first)
- File system traversal
- Database result set iteration
- Composite structure traversal
Modern Context: Most languages provide built-in iterators (Python generators, JavaScript iterators, C# IEnumerable).
---
5. Mediator - Centralize Communication Between Objects
Intent: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly.
When to Use:
- Set of objects communicate in well-defined but complex ways
- Reusing object is difficult due to dependencies on many others
- Behavior distributed between classes should be customizable without subclassing
- Reduce coupling between components
- Centralize complex interactions
When NOT to Use:
- Few objects interact simply
- Mediator becomes too complex (god object antipattern)
- Direct communication is clearer
Implementation Considerations:
- Mediator: interface for communicating with colleagues
- ConcreteMediator: implements cooperative behavior, knows colleagues
- Colleague: knows its mediator, communicates through it
- Two-way references (colleague to mediator, mediator to colleagues)
- Avoid mediator becoming monolithic
Example Use Cases:
- GUI dialog coordination (controls communicate through dialog)
- Chat rooms (users communicate through room)
- Air traffic control (planes coordinate through tower)
- Event buses and message brokers
- Game AI coordination
- Workflow engines
---
6. Memento - Capture and Restore Object State
Intent: Without violating encapsulation, capture and externalize an object's internal state so the object can be restored to this state later.
When to Use:
- Snapshot of object's state must be saved for later restoration
- Direct interface to obtain state would expose implementation and break encapsulation
- Implement undo/redo
- Checkpointing and rollback
- Save/restore game state
When NOT to Use:
- State is large (memory/performance concerns)
- State changes are infrequent
- Encapsulation is not a concern
Implementation Considerations:
- Memento: stores internal state of Originator
- Originator: creates memento containing snapshot, restores from memento
- Caretaker: responsible for memento's safekeeping
- Narrow interface: caretaker cannot examine memento contents
- Wide interface: originator can read/write memento
- Cost of saving state (memory footprint)
- Managing many mementos (history)
Example Use Cases:
- Undo/redo functionality
- Transaction rollback
- Game save states
- Editor snapshots
- Transaction logs
- Checkpoint/restore for long-running processes
---
7. Observer - Notify Multiple Objects of State Changes
Intent: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
When to Use:
- Abstraction has two aspects, one dependent on the other
- Change to one object requires changing others, number unknown
- Object should notify others without assumptions about who they are
- Implement event handling systems
- Data binding systems
When NOT to Use:
- Updates are very frequent (performance)
- Update logic is complex
- Observers and subjects create circular dependencies
Implementation Considerations:
- Subject: knows observers, provides attach/detach
- Observer: interface for objects to be notified
- ConcreteSubject: stores state, sends notifications
- ConcreteObserver: maintains reference to subject, implements update
- Push model: subject sends detailed information
- Pull model: subject sends minimal notification, observers pull data
- Event channels (observers subscribe to specific event types)
- Memory leaks (observers not unregistering)
Example Use Cases:
- Event handling systems
- Model-View-Controller (MVC)
- Data binding
- Publish-subscribe systems
- Reactive programming
- Real-time dashboards
- Notification systems
Modern Context: Reactive programming (RxJS, ReactiveX) is evolution of Observer.
---
8. State - Allow Object to Change Behavior with State
Intent: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
When to Use:
- Object behavior depends on state, must change at runtime
- Operations have large conditional statements dependent on state
- State-specific behavior should be independently defined
- State transitions are well-defined
When NOT to Use:
- State changes are rare
- Few states exist with simple transitions
- Conditional logic is straightforward
Implementation Considerations:
- Context: maintains current state, delegates to state object
- State: interface for encapsulating behavior
- ConcreteState: each implements behavior for particular state
- Context handles state transitions vs. states self-transitioning
- Shared state objects (flyweight) vs. unique instances
- State creation (pre-created vs. on-demand)
Example Use Cases:
- TCP connection states (Listen, Established, Closed)
- Order processing (Pending, Confirmed, Shipped, Delivered)
- Document workflows (Draft, Review, Approved, Published)
- Game character states (Idle, Walking, Jumping, Attacking)
- UI component states (Enabled, Disabled, Focused, Pressed)
- Traffic light states (Red, Yellow, Green)
---
9. Strategy - Use Interchangeable Algorithms
Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
When to Use:
- Many related classes differ only in behavior
- Need different variants of an algorithm
- Algorithm uses data client shouldn't know about
- Class defines many behaviors appearing as conditional statements
- Runtime algorithm selection
When NOT to Use:
- Algorithms rarely change
- Clients must understand all strategies to select one
- Simple conditional is clearer
Implementation Considerations:
- Strategy: interface common to all algorithms
- ConcreteStrategy: implements specific algorithm
- Context: maintains reference to strategy, uses strategy interface
- Strategy objects often stateless (can be flyweights)
- Client chooses strategy vs. context chooses strategy
- Default strategy if none specified
Example Use Cases:
- Sorting algorithms (quicksort, mergesort, heapsort)
- Compression algorithms
- Payment processing (credit card, PayPal, cryptocurrency)
- Validation rules
- Route finding algorithms
- Caching strategies
- Sorting/filtering strategies in UI
Strategy vs. State: Strategy client chooses algorithm (independent). State transitions automatically (may reference each other).
---
10. Template Method - Vary Algorithm Steps Through Subclassing
Intent: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps without changing the algorithm's structure.
When to Use:
- Implement invariant parts of algorithm once
- Let subclasses customize variable parts
- Common behavior among subclasses should be factored and centralized
- Control which operations subclasses can extend
- Avoid code duplication
When NOT to Use:
- Algorithm doesn't have invariant structure
- Inheritance is not appropriate
- Composition would be clearer (Strategy pattern)
Implementation Considerations:
- AbstractClass: defines template method and primitive operations
- ConcreteClass: implements primitive operations
- Abstract operations: must be implemented by subclasses
- Concrete operations: default behavior, may be overridden
- Hook operations: empty default, may be overridden
- Hollywood Principle: "Don't call us, we'll call you"
Example Use Cases:
- Framework classes (extend to customize)
- Test frameworks (setUp, tearDown hooks)
- Data processing pipelines (read, process, write)
- Lifecycle methods (initialization, execution, cleanup)
- Report generation
- Document processing
- Game loops
---
11. Visitor - Add Operations Without Changing Element Classes
Intent: Represent an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
When to Use:
- Object structure contains many classes with differing interfaces
- Many distinct and unrelated operations need to be performed
- Classes defining object structure rarely change, you add operations often
- Algorithm should work across several classes
- Need to perform complex operations on object structure
When NOT to Use:
- Object structure classes change frequently (visitor interface must change)
- Elements are simple and uniform
- Operations are closely tied to element classes
Implementation Considerations:
- Visitor: interface declaring visit operation for each ConcreteElement
- ConcreteVisitor: implements operations for each ConcreteElement
- Element: interface defining accept operation
- ConcreteElement: implements accept to call appropriate visitor method
- ObjectStructure: can enumerate elements
- Double dispatch (element type and visitor type determine operation)
- Breaking encapsulation (visitor may need access to element internals)
Example Use Cases:
- Compilers (AST traversal for optimization, code generation, type checking)
- File system operations (calculate size, search, backup)
- Export to different formats (XML, JSON, PDF)
- Reporting and analytics
- Object validation
- Pretty-printing
- Transformation operations
Visitor vs. Iterator: Visitor performs operation on each element. Iterator provides access to elements.
---
Key Relationships Between Behavioral Patterns
- Chain of Responsibility + Mediator: Mediator can route through chain
- Chain of Responsibility + Command: Commands passed through chain
- Command + Memento: Commands use mementos for undo
- Command + Prototype: Clone commands for undo/redo
- Mediator + Observer: Mediator can implement as observer pattern
- Memento + Iterator: Iterator can capture iteration state in memento
- Observer + Mediator: Mediator can use observer to notify colleagues
- State + Strategy: Similar structure, different intent
- Template Method + Strategy: Template Method uses inheritance, Strategy uses composition
- Visitor + Composite: Traverse composite structures performing operations
Implementation Guidelines
Language-Specific Notes
Rust: Use traits for interfaces, pattern matching for state transitions, closures for strategies and commands.
Python: Use ABC for interfaces, decorators for commands, classes for state objects, leverage duck typing.
C#: Use interfaces and delegates/events, async/await for asynchronous commands, properties for state.
TypeScript: Use interfaces, discriminated unions for state, function types for strategies.
Go: Use interfaces, function types for strategies, channels for observer patterns.
Dart: Use abstract classes, sealed classes for exhaustive state matching, streams for observer patterns.
General Principles
1. Single Responsibility: Each pattern component has one reason to change 2. Loose Coupling: Patterns reduce coupling through delegation and indirection 3. Open/Closed: Open for extension, closed for modification 4. Dependency Injection: Inject dependencies to enable pattern flexibility 5. Clear Contracts: Define interfaces clearly for pattern participants
---
See resources/language-guide.md for detailed language-specific implementations of these patterns.
Creational Design Patterns
Creational patterns abstract the instantiation process, making systems independent of how objects are created, composed, and represented. These patterns give you flexibility in what gets created, who creates it, how it gets created, and when.
1. Singleton - Ensure Single Instance with Global Access
Intent: Ensure a class has only one instance and provide a global point of access to it.
When to Use:
- Need exactly one instance of a class across the entire application
- Instance must be accessible from multiple points without passing references
- Instance is expensive to create (database connections, thread pools)
- Lazy initialization is beneficial (create only when first needed)
When NOT to Use:
- Need multiple instances with different configurations
- Global state creates tight coupling (consider dependency injection)
- Testing requires multiple instances or easy mocking
Implementation Considerations:
- Lazy initialization vs. eager (thread-safe variants)
- Thread-safe access (double-checked locking, locks, atomic operations)
- Language-specific: lazy_static in Rust, metaclass in Python, Lazy<T> in C#
- Registry-based singleton (multiple named instances)
Example Use Cases:
- Application configuration object
- Logger instances
- Database connection pools
- Thread pools
- Session managers
---
2. Factory Method - Defer Object Creation to Subclasses
Intent: Define an interface for creating an object, but let subclasses decide which class to instantiate.
When to Use:
- Class can't anticipate the type of objects it needs to create
- Class wants subclasses to specify objects to create
- Classes delegate responsibility to helper subclasses
- Want to localize knowledge of which concrete classes are used
When NOT to Use:
- Product types don't share a common interface
- Simple object creation suffices (direct instantiation is clearer)
- Need families of related products (use Abstract Factory instead)
Implementation Considerations:
- Abstract Creator class with abstract factory method
- ConcreteCreator subclasses implement factory method
- Clients work with abstract Product interface
- Can use parameterized factory methods (string or enum selects type)
Example Use Cases:
- Document editors (File > New creates specific document type)
- Framework classes (create appropriate logger for environment)
- Connection factories (create database-specific connections)
- Transport layers (HTTP, WebSocket, gRPC)
---
3. Abstract Factory - Create Families of Related Objects
Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
When to Use:
- System needs to work with multiple families of related products
- Product families must be used together consistently
- Want to provide a library of products and reveal interfaces only
- System should be independent of how products are created
When NOT to Use:
- Product families don't vary (single implementation)
- Adding new product types is more common than adding families (difficult to extend)
- Simple factory or factory method is sufficient
Implementation Considerations:
- AbstractFactory interface for creating each product family
- ConcreteFactory implements interface for specific family
- AbstractProduct interfaces for each product type
- Client works entirely with abstract interfaces
- Pairs well with Singleton (factory instances are often singletons)
Example Use Cases:
- UI framework (create family of Windows/Mac/Linux controls)
- Database support (create SQL Server/PostgreSQL/Oracle adapters)
- Theme engines (create coordinated colors, fonts, icons for theme)
- Device drivers (different implementations for Windows/Mac/Linux)
---
4. Builder - Separate Construction from Representation
Intent: Separate the construction of a complex object from its representation, allowing the same construction process to create different representations.
When to Use:
- Algorithm for creating complex objects should be independent of parts
- Construction process must allow different representations
- Object construction requires many steps or optional parameters
- Improve readability of complex constructor code (vs. many parameters)
When NOT to Use:
- Object construction is simple (use constructor directly)
- Product doesn't have complex construction process
- Object immutability with many parameters can be handled by factory methods
Implementation Considerations:
- Builder interface defines construction steps
- ConcreteBuilder implements steps, tracks representation
- Director controls construction sequence (optional, client can direct)
- Fluent Builder pattern: method chaining with return this
- Step Builder: enforces construction order at compile-time
- Can build without Director (client controls order)
Example Use Cases:
- Configuration objects (build with optional settings)
- Complex UI components (progressive construction)
- HTML/XML document construction
- Query builders (SQL, API requests)
- Immutable objects with many optional fields
---
5. Prototype - Clone Existing Objects Instead of Creating New
Intent: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
When to Use:
- Classes to instantiate are specified at runtime
- Avoiding building complex factory hierarchy parallel to product hierarchy
- Object instances can have only a few different state combinations
- Object creation is expensive (database lookups, network calls, computation)
- Need to decouple object creation from client code
When NOT to Use:
- Simple object creation is sufficient
- Deep copying is complex (circular references, resource handles)
- Language provides built-in cloning that's simpler
Implementation Considerations:
- Implement clone/copy method for copying protocol
- Shallow vs. deep copy (deep copy for complex objects)
- Handling circular references
- Managing resources (file handles, connections, memory)
- Registry of prototypes (by name/ID)
Example Use Cases:
- Cloning game objects (enemies, bullets with same configuration)
- Document templates (clone, then customize)
- Complex configuration objects
- Undo/redo (store clones as snapshots)
- Object pooling (clone from pool, reset state)
---
Key Relationships Between Creational Patterns
- Abstract Factory + Singleton: Factory instances are often singletons
- Abstract Factory + Prototype: Implement factories using prototype cloning
- Builder + Composite: Builder can construct composite structures step-by-step
- Factory Method + Strategy: Factory creates appropriate strategy objects
- Prototype + Composite: Clone complex composite structures
Implementation Guidelines
Language-Specific Notes
Rust: Use traits for products, leverage ownership system for Singleton (Arc/Mutex), use enums for type-safe cloning.
Python: Use metaclass for Singleton, ABC for formal interfaces, leverage duck typing for Factory.
C#: Use Lazy<T> for thread-safe Singleton, generics for type-safe patterns, properties for lazy initialization.
TypeScript: Use interfaces, private constructors, generics for type safety.
Go: Use sync.Once for thread-safe Singleton, function types for factory functions.
Dart: Use factory constructors for Factory patterns, sealed classes for Product variants.
General Principles
1. Follow Language Idioms: Use language-native features (decorators in Python, traits in Rust) 2. Type Safety: Use type systems to enforce patterns at compile-time where possible 3. Avoid God Objects: Keep factories focused on creation, not business logic 4. Document Parameters: Clear documentation of creation options and requirements 5. Error Handling: Handle invalid configurations, missing required parameters
---
See resources/language-guide.md for detailed language-specific implementations of these patterns.
Language-Specific Implementation Guide
This guide provides detailed implementation strategies for Gang of Four design patterns in each supported language.
Table of Contents
1. C# 2. Rust 3. Python 4. Dart 5. Go 6. GenAIScript 7. TypeScript 8. C
---
C#
Language Features Relevant to Patterns
- Interfaces: Define contracts for patterns
- Abstract Classes: Template Method, Factory Method base classes
- Properties: Encapsulation, lazy initialization
- Events: Observer pattern implementation
- Generics: Type-safe patterns (Repository, Factory)
- LINQ: Iterator, Visitor operations
- Async/Await: Asynchronous Command, Proxy
- Extension Methods: Decorator-like behavior
- Attributes: Metadata for pattern configuration
Pattern-Specific Guidance
Singleton
// Thread-safe lazy singleton using Lazy<T>
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance => lazy.Value;
private Singleton() { }
}Factory Method
// Use abstract class with generic constraints
public abstract class Creator<T> where T : Product
{
public abstract T FactoryMethod();
public void SomeOperation()
{
var product = FactoryMethod();
product.DoSomething();
}
}Observer
// Use events and delegates
public class Subject
{
public event EventHandler<StateChangedEventArgs> StateChanged;
protected virtual void OnStateChanged(StateChangedEventArgs e)
{
StateChanged?.Invoke(this, e);
}
}Decorator
// Use interfaces and composition
public interface IComponent
{
void Operation();
}
public class Decorator : IComponent
{
private readonly IComponent _component;
public Decorator(IComponent component)
{
_component = component;
}
public virtual void Operation()
{
_component.Operation();
}
}Naming Conventions
- PascalCase for classes, interfaces, methods, properties
- Prefix interfaces with 'I' (IComponent, IObserver)
- Use meaningful names (ConcreteDecoratorA → BorderDecorator)
Best Practices
- Use dependency injection containers (Microsoft.Extensions.DependencyInjection)
- Prefer async/await for asynchronous operations
- Use IDisposable for resource cleanup in patterns
- Leverage LINQ for collection operations in Iterator
- Use Expression-bodied members for simple implementations
---
Rust
Language Features Relevant to Patterns
- Traits: Define interfaces for patterns
- Enums: Sum types for State pattern
- Struct: Data structures for patterns
- Ownership: Singleton, Prototype considerations
- Arc/Mutex: Thread-safe shared state
- Box/Rc: Heap allocation, reference counting
- Pattern Matching: State, Strategy dispatch
- Closures: Command, Strategy patterns
- Lifetimes: Managing references in patterns
Pattern-Specific Guidance
Singleton
// Using lazy_static or once_cell
use std::sync::{Arc, Mutex};
use once_cell::sync::Lazy;
static INSTANCE: Lazy<Arc<Mutex<Singleton>>> = Lazy::new(|| {
Arc::new(Mutex::new(Singleton::new()))
});
struct Singleton {
// fields
}
impl Singleton {
fn new() -> Self {
Singleton { /* ... */ }
}
fn instance() -> Arc<Mutex<Singleton>> {
Arc::clone(&INSTANCE)
}
}Factory Method
// Use traits for product and creator
trait Product {
fn operation(&self);
}
trait Creator {
type ProductType: Product;
fn factory_method(&self) -> Self::ProductType;
fn some_operation(&self) {
let product = self.factory_method();
product.operation();
}
}State
// Use enums for type-safe states
enum State {
Idle,
Running { data: String },
Finished,
}
struct Context {
state: State,
}
impl Context {
fn handle(&mut self) {
self.state = match self.state {
State::Idle => State::Running { data: String::new() },
State::Running { ref data } => State::Finished,
State::Finished => State::Idle,
};
}
}Strategy
// Use trait objects for runtime polymorphism
trait Strategy {
fn execute(&self, data: &str) -> String;
}
struct Context {
strategy: Box<dyn Strategy>,
}
impl Context {
fn execute_strategy(&self, data: &str) -> String {
self.strategy.execute(data)
}
}Naming Conventions
- snake_case for functions, variables, modules
- PascalCase for types, traits, enums
- SCREAMING_SNAKE_CASE for constants
- Descriptive trait names (Drawable, Cloneable, Strategy)
Best Practices
- Use newtype pattern for type safety
- Leverage enums for State pattern (type-safe states)
- Use Arc<Mutex<T>> for shared mutable state
- Prefer composition over complex inheritance
- Use trait objects (dyn Trait) for runtime polymorphism
- Use generic traits for compile-time polymorphism
- Handle errors with Result<T, E>
- Document ownership transfer in comments
---
Python
Language Features Relevant to Patterns
- ABC (Abstract Base Classes): Formal interfaces
- Duck Typing: Informal interfaces
- Decorators: Decorator pattern, function wrapping
- Properties: Encapsulation, computed attributes
- Magic Methods: Operator overloading, iteration
- Metaclasses: Singleton, class creation patterns
- Multiple Inheritance: Mixin patterns
- Type Hints: Static type checking
- Generators: Iterator pattern
- Context Managers: Resource management in patterns
Pattern-Specific Guidance
Singleton
# Using metaclass
class SingletonMeta(type):
_instances = {}
def __call__(cls, *args, **kwargs):
if cls not in cls._instances:
cls._instances[cls] = super().__call__(*args, **kwargs)
return cls._instances[cls]
class Singleton(metaclass=SingletonMeta):
pass
# Or using decorator
def singleton(cls):
instances = {}
def get_instance(*args, **kwargs):
if cls not in instances:
instances[cls] = cls(*args, **kwargs)
return instances[cls]
return get_instance
@singleton
class Singleton:
passFactory Method
from abc import ABC, abstractmethod
class Creator(ABC):
@abstractmethod
def factory_method(self) -> Product:
pass
def some_operation(self) -> str:
product = self.factory_method()
return product.operation()Decorator
# Using function decorators
def logging_decorator(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__}")
result = func(*args, **kwargs)
print(f"Finished {func.__name__}")
return result
return wrapper
# Using class-based decorator
class Component(ABC):
@abstractmethod
def operation(self) -> str:
pass
class Decorator(Component):
def __init__(self, component: Component):
self._component = component
def operation(self) -> str:
return self._component.operation()Observer
# Using properties and callbacks
class Subject:
def __init__(self):
self._observers = []
self._state = None
def attach(self, observer):
self._observers.append(observer)
def notify(self):
for observer in self._observers:
observer.update(self)
@property
def state(self):
return self._state
@state.setter
def state(self, value):
self._state = value
self.notify()Naming Conventions
- snake_case for functions, variables, modules
- PascalCase for classes
- ALL_CAPS for constants
- Private members prefixed with underscore (_private)
- Dunder methods for magic methods (__init__, __str__)
Best Practices
- Use ABC for formal interfaces when needed
- Leverage duck typing for flexibility
- Use type hints (from typing module) for clarity
- Prefer composition over inheritance
- Use __slots__ for memory efficiency in Flyweight
- Use context managers (with statement) for resource management
- Leverage generators for Iterator pattern
- Use dataclasses for simple data structures
- Follow PEP 8 style guide
---
Dart
Language Features Relevant to Patterns
- Abstract Classes: Define contracts
- Interfaces: Implicit (every class is an interface)
- Mixins: Share behavior across classes
- Factory Constructors: Named constructors for factories
- Getters/Setters: Computed properties
- Generics: Type-safe patterns
- Async/Await: Asynchronous operations
- Streams: Observer pattern implementation
- Extension Methods: Add functionality to existing classes
- Sealed Classes: Exhaustive pattern matching (Dart 3+)
Pattern-Specific Guidance
Singleton
class Singleton {
// Private constructor
Singleton._internal();
// Static instance
static final Singleton _instance = Singleton._internal();
// Factory constructor returns the single instance
factory Singleton() {
return _instance;
}
}
// Usage: var singleton = Singleton();Factory Method
abstract class Product {
void operation();
}
abstract class Creator {
// Factory method
Product factoryMethod();
void someOperation() {
final product = factoryMethod();
product.operation();
}
}
class ConcreteCreator extends Creator {
@override
Product factoryMethod() => ConcreteProduct();
}Builder
// Using cascade notation
class Product {
String? partA;
String? partB;
Product();
}
class Builder {
final Product _product = Product();
Builder setPartA(String value) {
_product.partA = value;
return this;
}
Builder setPartB(String value) {
_product.partB = value;
return this;
}
Product build() => _product;
}
// Usage with cascade:
// var product = Builder()
// ..setPartA('A')
// ..setPartB('B')
// ..build();Observer (using Streams)
import 'dart:async';
class Subject {
final _controller = StreamController<int>.broadcast();
Stream<int> get stream => _controller.stream;
void updateState(int state) {
_controller.add(state);
}
void dispose() {
_controller.close();
}
}
// Usage:
// subject.stream.listen((state) => print(state));Naming Conventions
- lowerCamelCase for variables, functions, parameters
- UpperCamelCase for classes, enums, type definitions
- lowercase_with_underscores for libraries, packages
- Prefix private members with underscore (_private)
Best Practices
- Use factory constructors for Factory patterns
- Leverage named constructors for different creation methods
- Use const constructors for immutable objects (Flyweight)
- Use mixins for shared behavior without inheritance
- Use Streams for Observer pattern (reactive programming)
- Leverage async/await for asynchronous patterns
- Use extension methods to add functionality without inheritance
- Follow Effective Dart guidelines
- Use sealed classes (Dart 3+) for exhaustive State pattern matching
---
Go
Language Features Relevant to Patterns
- Interfaces: Implicit implementation
- Structs: Data structures
- Struct Embedding: Composition
- Function Types: First-class functions
- Closures: Capture state for patterns
- Goroutines: Concurrent patterns
- Channels: Communication, Observer pattern
- sync Package: Synchronization primitives
- defer: Resource cleanup
Pattern-Specific Guidance
Singleton
package singleton
import "sync"
type singleton struct {
// fields
}
var instance *singleton
var once sync.Once
func GetInstance() *singleton {
once.Do(func() {
instance = &singleton{}
})
return instance
}Factory Method
// Use interface and factory functions
type Product interface {
Operation() string
}
type ConcreteProductA struct{}
func (p *ConcreteProductA) Operation() string {
return "Product A"
}
type Creator interface {
FactoryMethod() Product
}
type ConcreteCreator struct{}
func (c *ConcreteCreator) FactoryMethod() Product {
return &ConcreteProductA{}
}
// Or use function types
type FactoryFunc func() Product
func CreateProductA() Product {
return &ConcreteProductA{}
}Strategy
// Use function types
type Strategy func(data string) string
type Context struct {
strategy Strategy
}
func (c *Context) ExecuteStrategy(data string) string {
return c.strategy(data)
}
// Or use interfaces
type Strategy interface {
Execute(data string) string
}
type Context struct {
strategy Strategy
}Observer (using channels)
type Subject struct {
observers []chan int
}
func (s *Subject) Attach(observer chan int) {
s.observers = append(s.observers, observer)
}
func (s *Subject) Notify(state int) {
for _, observer := range s.observers {
observer <- state
}
}Decorator
// Use struct embedding
type Component interface {
Operation() string
}
type ConcreteComponent struct{}
func (c *ConcreteComponent) Operation() string {
return "ConcreteComponent"
}
type Decorator struct {
Component
}
func (d *Decorator) Operation() string {
return "Decorator(" + d.Component.Operation() + ")"
}Naming Conventions
- MixedCase for exported names (public)
- mixedCase for unexported names (private)
- Acronyms are all caps (HTTP, URL, ID)
- Interface names often end in -er (Reader, Writer, Strategy)
- Package names are lowercase, single word
Best Practices
- Accept interfaces, return structs
- Keep interfaces small (single method common)
- Use struct embedding for composition
- Use sync.Once for thread-safe Singleton
- Leverage function types for Strategy and Command
- Use channels for Observer pattern communication
- Use defer for cleanup in patterns (Close, Unlock)
- Error handling with multiple return values
- Use context.Context for cancellation and timeouts
- Follow Go Code Review Comments guidelines
---
GenAIScript
Language Features Relevant to Patterns
GenAIScript is JavaScript/TypeScript-based, so it inherits those features:
- Functions as First-Class Citizens: Strategy, Command
- Closures: Encapsulation, state capture
- Prototypes/Classes: OOP patterns
- Async/Await: Asynchronous patterns
- Promises: Future/Promise pattern
- Object Literals: Simple object creation
- Destructuring: Clean parameter handling
- Modules: Namespace isolation
Pattern-Specific Guidance
Singleton
// Using module pattern
const Singleton = (() => {
let instance;
function createInstance() {
return {
// properties and methods
};
}
return {
getInstance: () => {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
// Or ES6 class
class Singleton {
constructor() {
if (Singleton.instance) {
return Singleton.instance;
}
Singleton.instance = this;
}
}Factory Method
class Creator {
factoryMethod() {
throw new Error("Must be implemented");
}
someOperation() {
const product = this.factoryMethod();
return product.operation();
}
}
class ConcreteCreator extends Creator {
factoryMethod() {
return new ConcreteProduct();
}
}Strategy
// Using function objects
class Context {
constructor(strategy) {
this.strategy = strategy;
}
executeStrategy(data) {
return this.strategy(data);
}
}
// Usage
const context = new Context((data) => data.toUpperCase());Observer
class Subject {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
notify(data) {
this.observers.forEach(observer => observer.update(data));
}
}
// Or use EventEmitter patternNaming Conventions
- camelCase for variables, functions
- PascalCase for classes, constructors
- UPPER_CASE for constants
- Prefix private fields with # (ES2022+) or underscore
Best Practices
- Use modern JavaScript/TypeScript features
- Leverage async/await for asynchronous operations
- Use arrow functions for concise syntax
- Destructure parameters for clarity
- Use const/let instead of var
- Leverage built-in methods (map, filter, reduce)
- Use modules for encapsulation
- Follow JavaScript Standard Style or similar
---
TypeScript
Language Features Relevant to Patterns
- Interfaces: Define contracts
- Type Aliases: Union types, intersections
- Classes: OOP patterns
- Generics: Type-safe patterns
- Abstract Classes: Template Method, Factory Method
- Decorators: Metadata, AOP patterns
- Enums: State enumeration
- Union Types: State representation
- Type Guards: Runtime type checking
- Access Modifiers: private, protected, public
Pattern-Specific Guidance
Singleton
class Singleton {
private static instance: Singleton;
private constructor() {
// Private constructor
}
public static getInstance(): Singleton {
if (!Singleton.instance) {
Singleton.instance = new Singleton();
}
return Singleton.instance;
}
}Factory Method
interface Product {
operation(): string;
}
abstract class Creator {
abstract factoryMethod(): Product;
someOperation(): string {
const product = this.factoryMethod();
return product.operation();
}
}
class ConcreteCreator extends Creator {
factoryMethod(): Product {
return new ConcreteProduct();
}
}Builder
class Product {
partA?: string;
partB?: number;
}
class Builder {
private product: Product = new Product();
setPartA(value: string): this {
this.product.partA = value;
return this;
}
setPartB(value: number): this {
this.product.partB = value;
return this;
}
build(): Product {
return this.product;
}
}
// Usage: const product = new Builder().setPartA('A').setPartB(1).build();State (using union types)
type State =
| { type: 'idle' }
| { type: 'loading'; progress: number }
| { type: 'success'; data: string }
| { type: 'error'; error: Error };
class Context {
private state: State = { type: 'idle' };
setState(state: State) {
this.state = state;
}
handle() {
switch (this.state.type) {
case 'idle':
// Handle idle
break;
case 'loading':
// Access state.progress
break;
case 'success':
// Access state.data
break;
case 'error':
// Access state.error
break;
}
}
}Decorator (using TypeScript decorators)
function Logger(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const original = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey}`);
const result = original.apply(this, args);
console.log(`Finished ${propertyKey}`);
return result;
};
return descriptor;
}
class Example {
@Logger
method() {
console.log('Executing method');
}
}Naming Conventions
- camelCase for variables, functions, properties
- PascalCase for classes, interfaces, types, enums
- UPPER_CASE for constants
- Prefix interfaces with 'I' (optional, style preference)
- Use meaningful names for type parameters (TKey, TValue vs. T, U)
Best Practices
- Use strict mode (strict: true in tsconfig.json)
- Leverage type inference where possible
- Use generics for reusable, type-safe code
- Prefer interfaces for object shapes, types for unions/intersections
- Use readonly for immutability
- Leverage discriminated unions for State pattern
- Use utility types (Partial, Required, Pick, Omit)
- Use enums sparingly (prefer union types)
- Follow TypeScript official style guide
- Use null safety (strictNullChecks)
---
C
Language Features Relevant to Patterns
C lacks OOP features, so patterns require different implementations:
- Structs: Data structures
- Function Pointers: Polymorphism, callbacks
- Opaque Pointers: Encapsulation
- Static Variables: Singleton, class-level state
- Macros: Code generation
- Void Pointers: Generic programming
- Header/Implementation Split: Interface definition
Pattern-Specific Guidance
Singleton
// singleton.h
typedef struct Singleton Singleton;
Singleton* singleton_get_instance(void);
void singleton_do_something(Singleton* self);
// singleton.c
struct Singleton {
int data;
};
static Singleton* instance = NULL;
Singleton* singleton_get_instance(void) {
if (instance == NULL) {
instance = malloc(sizeof(Singleton));
instance->data = 0;
}
return instance;
}Factory Method (using function pointers)
// Product interface (function pointers)
typedef struct Product {
void (*operation)(struct Product* self);
void (*destroy)(struct Product* self);
} Product;
// Factory function type
typedef Product* (*FactoryFunc)(void);
// Creator
typedef struct Creator {
FactoryFunc factory_method;
} Creator;
Product* creator_some_operation(Creator* creator) {
Product* product = creator->factory_method();
product->operation(product);
return product;
}Strategy
// Strategy function type
typedef int (*StrategyFunc)(const char* data);
typedef struct Context {
StrategyFunc strategy;
} Context;
void context_execute_strategy(Context* ctx, const char* data) {
int result = ctx->strategy(data);
// Use result
}
// Concrete strategies
int strategy_a(const char* data) {
// Implementation A
return 0;
}
int strategy_b(const char* data) {
// Implementation B
return 1;
}Polymorphism (vtable pattern)
// Base class
typedef struct Shape {
const struct ShapeVTable* vtable;
} Shape;
typedef struct ShapeVTable {
void (*draw)(Shape* self);
void (*destroy)(Shape* self);
} ShapeVTable;
// Concrete class
typedef struct Circle {
Shape base;
int radius;
} Circle;
void circle_draw(Shape* self) {
Circle* circle = (Circle*)self;
printf("Circle with radius %d\n", circle->radius);
}
void circle_destroy(Shape* self) {
free(self);
}
static const ShapeVTable circle_vtable = {
.draw = circle_draw,
.destroy = circle_destroy,
};
Circle* circle_create(int radius) {
Circle* circle = malloc(sizeof(Circle));
circle->base.vtable = &circle_vtable;
circle->radius = radius;
return circle;
}Opaque Pointers (encapsulation)
// header.h
typedef struct Object Object; // Opaque type
Object* object_create(void);
void object_do_something(Object* obj);
void object_destroy(Object* obj);
// implementation.c
struct Object {
int private_data;
char* private_string;
};
Object* object_create(void) {
Object* obj = malloc(sizeof(Object));
obj->private_data = 0;
obj->private_string = NULL;
return obj;
}Naming Conventions
- snake_case for functions, variables
- UPPER_CASE for macros, constants
- PascalCase or snake_case for types (struct Circle or struct circle)
- Prefix functions with module name (list_create, list_add)
- Prefix struct members to avoid conflicts
Best Practices
- Use opaque pointers for encapsulation
- Function pointers for polymorphism and callbacks
- Naming conventions to namespace (module_function)
- Always check malloc return values
- Provide create/destroy functions (constructors/destructors)
- Use const for read-only parameters
- Document ownership and lifetime in comments
- Use static for internal functions (file scope)
- Header guards for all headers
- Follow a consistent coding standard (K&R, Linux kernel style)
Memory Management
- Patterns must handle memory explicitly
- Provide destroy/cleanup functions
- Document who owns allocated memory
- Use reference counting for shared objects
- Consider using memory pools for Flyweight
---
Cross-Language Considerations
When Implementing Patterns
1. Respect Language Idioms
- Don't force OOP patterns in functional languages
- Use language-native features (decorators in Python, traits in Rust)
- Follow community conventions
2. Type Systems
- Static typing: Use generics, interfaces
- Dynamic typing: Use duck typing, protocols
- Leverage type system for compile-time safety
3. Memory Management
- GC languages: Focus on references and lifetime
- Manual management: Document ownership, provide cleanup
- Rust: Leverage ownership system for safety
4. Concurrency
- Thread-safe Singleton in multithreaded environments
- Use language-appropriate synchronization (locks, channels, actors)
- Consider immutability for concurrent patterns
5. Error Handling
- Exceptions: try/catch in C#, Python, TypeScript
- Result types: Result<T, E> in Rust
- Multiple returns: value, error in Go
- Return codes: int status in C
6. Testing
- Patterns should be testable
- Use dependency injection
- Provide interfaces for mocking
- Document test strategies
---
This guide should be consulted when implementing patterns in specific languages to ensure idiomatic, production-quality code.
Design Pattern Selection Guide
This guide provides a decision tree and reference table to help you select the most appropriate Gang of Four design pattern for your problem.
Quick Decision Table
| Your Need | Pattern(s) | Category | When to Use |
|---|---|---|---|
| Need only one instance globally | Singleton | Creational | Configuration, loggers, connection pools |
| Create objects without specifying classes | Factory Method | Creational | Decouple creation, allow subclasses to choose |
| Create families of related objects | Abstract Factory | Creational | UI frameworks, database drivers |
| Complex object construction | Builder | Creational | Configuration objects, fluent APIs |
| Clone expensive objects | Prototype | Creational | Expensive object creation, templates |
| Adapt incompatible interfaces | Adapter | Structural | Legacy integration, third-party libraries |
| Decouple abstraction from implementation | Bridge | Structural | OS-specific code, rendering engines |
| Part-whole hierarchies (trees) | Composite | Structural | File systems, UI, organizational charts |
| Add behavior dynamically | Decorator | Structural | Wrapping objects, middleware, streams |
| Simplify complex subsystem | Facade | Structural | Framework initialization, simplified APIs |
| Share many fine-grained objects | Flyweight | Structural | Large object counts, memory optimization |
| Control access to objects | Proxy | Structural | Lazy loading, remote objects, access control |
| Pass request through handlers | Chain of Responsibility | Behavioral | Event handling, logging, approval workflows |
| Encapsulate actions as objects | Command | Behavioral | Undo/redo, queuing, remote invocation |
| Parse custom languages/grammars | Interpreter | Behavioral | Expression evaluation, DSLs, query builders |
| Traverse collections uniformly | Iterator | Behavioral | Collection access, tree/graph traversal |
| Centralize complex interactions | Mediator | Behavioral | Dialog coordination, event buses |
| Capture and restore state | Memento | Behavioral | Undo/redo, savepoints, transactions |
| Notify many objects of changes | Observer | Behavioral | Event systems, reactive programming, data binding |
| Object behavior varies by state | State | Behavioral | TCP states, order workflows, game states |
| Interchangeable algorithms | Strategy | Behavioral | Sorting, compression, validation algorithms |
| Vary algorithm steps | Template Method | Behavioral | Framework hooks, lifecycle methods |
| Add operations to object structures | Visitor | Behavioral | Compilers, reporting, transformations |
---
Decision Tree: What Problem Are You Solving?
Step 1: Problem Category
Are you solving an object creation problem?
→ YES: Go to Creational Patterns (below)
→ NO: Continue
Are you solving an object composition problem?
→ YES: Go to Structural Patterns (below)
→ NO: Continue
Are you solving an object communication/responsibility problem?
→ YES: Go to Behavioral Patterns (below)
→ NO: You may not need a GoF patternCreational Patterns Decision Tree
How do you need to create objects?
Do you need EXACTLY ONE instance?
→ YES: Use SINGLETON
→ NO: Continue
Do you need to create DIFFERENT TYPES at runtime?
→ YES: Is it a FAMILY of related objects?
→ YES: Use ABSTRACT FACTORY
→ NO: Use FACTORY METHOD
→ NO: Continue
Is the construction process COMPLEX with many steps?
→ YES: Use BUILDER
→ NO: Continue
Is object creation EXPENSIVE and you want to COPY existing objects?
→ YES: Use PROTOTYPE
→ NO: Simple constructors are sufficientStructural Patterns Decision Tree
How do you need to compose objects/classes?
Do you need to adapt INCOMPATIBLE INTERFACES?
→ YES: Use ADAPTER
→ NO: Continue
Do you want SEPARATE abstraction and implementation hierarchies?
→ YES: Use BRIDGE
→ NO: Continue
Do you need to represent PART-WHOLE HIERARCHIES (trees)?
→ YES: Use COMPOSITE
→ NO: Continue
Do you want to ADD RESPONSIBILITIES DYNAMICALLY to individual objects?
→ YES: Use DECORATOR
→ NO: Continue
Do you want to SIMPLIFY ACCESS to complex subsystems?
→ YES: Use FACADE
→ NO: Continue
Do you need to create MANY SIMILAR OBJECTS and memory is constrained?
→ YES: Use FLYWEIGHT
→ NO: Continue
Do you want to CONTROL ACCESS to another object?
→ YES: Use PROXY
→ NO: Simple composition/delegation is sufficientBehavioral Patterns Decision Tree
How should objects communicate?
Do you want to PASS REQUESTS through a chain of handlers?
→ YES: Use CHAIN OF RESPONSIBILITY
→ NO: Continue
Do you want to ENCAPSULATE REQUESTS as objects?
→ YES: Use COMMAND
→ NO: Continue
Do you need to INTERPRET CUSTOM LANGUAGES/GRAMMARS?
→ YES: Use INTERPRETER
→ NO: Continue
Do you want to ACCESS ELEMENTS without exposing the collection?
→ YES: Use ITERATOR
→ NO: Continue
Do you want to CENTRALIZE COMPLEX INTERACTIONS between objects?
→ YES: Use MEDIATOR
→ NO: Continue
Do you want to CAPTURE/RESTORE OBJECT STATE without breaking encapsulation?
→ YES: Use MEMENTO
→ NO: Continue
Do you want to NOTIFY MULTIPLE OBJECTS of state changes?
→ YES: Use OBSERVER
→ NO: Continue
Does object behavior CHANGE BASED ON INTERNAL STATE?
→ YES: Use STATE
→ NO: Continue
Do you need INTERCHANGEABLE ALGORITHMS that client selects?
→ YES: Use STRATEGY
→ NO: Continue
Do you want to VARY ALGORITHM STEPS through subclassing?
→ YES: Use TEMPLATE METHOD
→ NO: Continue
Do you want to ADD OPERATIONS to an object structure?
→ YES: Use VISITOR
→ NO: Simple methods are sufficient---
Pattern Selection by Problem Domain
Web Applications
| Problem | Pattern | Example |
|---|---|---|
| User authentication persistence | Singleton | AuthenticationManager |
| Creating different page types | Factory Method | PageFactory |
| UI component styling | Decorator | StyledButton (Button + BorderDecorator) |
| State machine for forms | State | FormStateManager (Idle → Filled → Validating → Submitted) |
| Event handling | Observer | FormEventBus |
| Complex queries | Builder | QueryBuilder for SQL/API |
| API response transformation | Visitor | ResponseVisitor (JSON/XML/CSV) |
Game Development
| Problem | Pattern | Example |
|---|---|---|
| Single game manager | Singleton | GameManager |
| Creating game objects | Factory Method | EnemyFactory |
| Game entity behaviors | Strategy | MovementStrategy (patrol, chase, flee) |
| Character states | State | CharacterState (idle, running, jumping) |
| UI element hierarchy | Composite | UIPanel (contains other panels and buttons) |
| Particle effects composition | Decorator | ParticleEffect (base + AdditiveBlend + Rotation) |
| Event system | Observer | EventBus (subscribers listen to events) |
| Saving game state | Memento | GameSaveSnapshot |
Enterprise Applications
| Problem | Pattern | Example |
|---|---|---|
| Database connections | Singleton | ConnectionPool |
| Different database types | Abstract Factory | DatabaseFactory (SQL Server, PostgreSQL, Oracle) |
| Complex order creation | Builder | OrderBuilder |
| Workflow approval chain | Chain of Responsibility | ApprovalChain |
| Business operations | Command | Transaction (for undo/redo, logging) |
| System monitoring | Observer | EventLog (listens to system events) |
| Configuration objects | Singleton | AppConfiguration |
| Export formats | Visitor | ReportVisitor (PDF, Excel, JSON) |
Data Processing
| Problem | Pattern | Example |
|---|---|---|
| Multi-step data pipeline | Template Method | DataProcessingTemplate (read, transform, write) |
| Different compression algorithms | Strategy | CompressionStrategy (zip, gzip, bzip2) |
| Tree structure processing | Composite | FileSystemNode (files and directories) |
| Complex queries | Interpreter | QueryExpression (boolean logic, comparisons) |
| Collection traversal | Iterator | TreeIterator (depth-first, breadth-first) |
| Data validation rules | Strategy | ValidationStrategy (email, phone, custom) |
System Architecture
| Problem | Pattern | Example |
|---|---|---|
| Single entry point | Facade | SystemAPI |
| Platform-specific implementation | Bridge | GraphicsDriver (Windows, Mac, Linux) |
| Centralized object creation | Factory Method | ServiceFactory |
| Permission-based access | Proxy | SecureDocumentProxy |
| Feature toggling | Decorator | FeatureDecorator |
| Caching layer | Proxy | CacheProxy |
---
Combining Patterns (Common Compositions)
Many problems benefit from using multiple patterns together:
Creative Problem-Solving
1. Factory + Builder: Factory creates builders, builder constructs complex objects 2. Factory + Strategy: Factory creates appropriate strategy implementations 3. Abstract Factory + Singleton: Singleton factory instances for product families
Complex State Management
1. State + Strategy: State pattern for transitions, strategy for behavior variation 2. Memento + Command: Commands store mementos for undo/redo 3. State + Composite: States for multi-level state machines
Flexible System Architecture
1. Facade + Singleton: Single facade instance for subsystem access 2. Bridge + Strategy: Separate implementation hierarchy with algorithmic variation 3. Proxy + Decorator: Proxy for access control, decorator for behavior addition
Event-Driven Systems
1. Observer + Mediator: Mediator uses observer to notify colleagues 2. Chain of Responsibility + Observer: Events propagate through chain with observers 3. Command + Observer: Commands trigger observer notifications
Complex Data Structures
1. Composite + Iterator: Traverse tree structures uniformly 2. Composite + Visitor: Perform operations on tree elements 3. Iterator + Memento: Iterator captures state in memento
Extensible Frameworks
1. Template Method + Strategy: Template defines structure, strategies vary algorithms 2. Factory Method + Template Method: Factory creates subclasses that use template method 3. Decorator + Factory: Factory creates decorated objects
---
Anti-Patterns: When NOT to Use Patterns
Premature Abstraction
- Problem: Applying patterns before they're needed
- Solution: Refactor toward patterns when need arises (YAGNI principle)
Over-Engineering
- Problem: Using complex patterns for simple problems
- Solution: Start simple, add patterns as complexity demands
God Objects
- Problem: Mediator, Facade becoming monolithic dumping grounds
- Solution: Split into smaller mediators/facades, use multiple patterns
Singleton Abuse
- Problem: Overuse creates global state, hides dependencies, hard to test
- Solution: Use dependency injection, limit to true singletons
Pattern Overloading
- Problem: Too many patterns in one class
- Solution: Each class should primarily follow one pattern
---
Selecting Patterns for Your Language
Idiomatic Patterns by Language
Python
- Leverage metaclasses (Singleton, ABCs)
- Use decorators naturally (@decorator syntax)
- Duck typing reduces need for many structural patterns
- Generators for Iterator pattern
Rust
- Traits for interfaces and polymorphism
- Enums for type-safe State pattern
- Arc/Mutex for thread-safe Singleton
- Pattern matching for command dispatch
C#
- Events/delegates for Observer pattern
- Generics for type-safe factories
- Lazy<T> for thread-safe Singleton
- Async/await for asynchronous patterns
TypeScript
- Union types for State pattern (discriminated unions)
- Decorators for metadata patterns
- Function types for Strategy/Command
- Generics for type safety
Go
- Implicit interface implementation
- Function types for Strategy/Command
- sync.Once for thread-safe Singleton
- Channels for Observer pattern
Dart
- Factory constructors for Factory patterns
- Sealed classes for exhaustive State matching
- Streams for Observer pattern
- Cascade notation for Builder patterns
---
Pattern Learning Path
If you're new to design patterns, learn them in this order:
1. Factory Method - Simple pattern, widely used, easy to understand 2. Observer - Fundamental to event-driven systems 3. Decorator - Teaches composition over inheritance 4. Strategy - Teaches parameterizing behavior 5. Template Method - Teaches skeletal structure 6. Singleton - Common but use carefully 7. Adapter - Essential for integration 8. Composite - Teaches recursive structures 9. State - More advanced state management 10. Command - Teaches encapsulating actions 11. Iterator - Teaches uniform traversal 12. Proxy - Teaches access control/lazy loading 13. Builder - For complex construction 14. Chain of Responsibility - For request routing 15. Facade - For API simplification 16. Bridge - For abstraction/implementation separation 17. Mediator - For complex coordination 18. Memento - For state capture 19. Interpreter - For custom languages 20. Visitor - For operation addition 21. Prototype - For cloning 22. Abstract Factory - Complex factory variant 23. Flyweight - Memory optimization pattern
---
See the category resource files for detailed pattern documentation:
resources/creational-patterns.md- Pattern detailsresources/structural-patterns.md- Pattern detailsresources/behavioral-patterns.md- Pattern detailsresources/language-guide.md- Language-specific implementation
Gang of Four Design Patterns Reference
This document provides detailed descriptions of all 23 Gang of Four design patterns.
Creational Patterns
Creational patterns abstract the instantiation process, making systems independent of how objects are created, composed, and represented.
1. Singleton
Intent: Ensure a class has only one instance and provide a global point of access to it.
Problem: You need exactly one instance of a class, and it must be accessible from a well-known access point.
Solution: Make the class responsible for keeping track of its sole instance. Intercept requests to create new instances and return the existing one.
Structure:
- Private constructor prevents direct instantiation
- Static method returns the singleton instance
- Static variable holds the single instance
Use When:
- There must be exactly one instance of a class
- The instance must be accessible from multiple points
- The sole instance should be extensible by subclassing
Avoid When:
- You need multiple instances with different configurations
- Global state creates tight coupling (consider dependency injection instead)
- Testing requires multiple instances or mocking
Common Implementations:
- Lazy initialization (create on first access)
- Eager initialization (create at program start)
- Thread-safe variants (double-checked locking, locks)
- Registry-based (multiple named instances)
---
2. Factory Method
Intent: Define an interface for creating an object, but let subclasses decide which class to instantiate.
Problem: A class can't anticipate the type of objects it needs to create.
Solution: Define an interface for creating objects, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.
Structure:
- Creator (abstract class with factory method)
- ConcreteCreator (implements factory method)
- Product (interface for objects)
- ConcreteProduct (implements product interface)
Use When:
- A class can't anticipate the class of objects to create
- A class wants subclasses to specify objects to create
- Classes delegate responsibility to helper subclasses
- You want to localize knowledge of which class gets created
Avoid When:
- Product types don't share a common interface
- Simple object creation suffices
- You need families of related products (use Abstract Factory)
---
3. Abstract Factory
Intent: Provide an interface for creating families of related or dependent objects without specifying their concrete classes.
Problem: You need to create families of related objects that must be used together.
Solution: Declare interfaces for creating each product. Concrete factories implement these interfaces to create product families.
Structure:
- AbstractFactory (interface for creating products)
- ConcreteFactory (creates specific product families)
- AbstractProduct (interface for product types)
- ConcreteProduct (specific product implementations)
- Client (uses only abstract interfaces)
Use When:
- System should be independent of how products are created
- System should work with multiple families of products
- Family of related products must be used together
- You want to provide a library of products and reveal interfaces only
Avoid When:
- Product families don't vary
- Adding new product types is more common than adding families (difficult to extend)
- Simple factory or factory method is sufficient
---
4. Builder
Intent: Separate the construction of a complex object from its representation, allowing the same construction process to create different representations.
Problem: Creating a complex object with many optional parts or configuration steps.
Solution: Extract object construction code into separate builder objects. Direct the construction using a director class.
Structure:
- Builder (interface for creating product parts)
- ConcreteBuilder (implements builder interface, tracks representation)
- Director (constructs object using builder interface)
- Product (complex object being built)
Use When:
- Algorithm for creating complex objects should be independent of parts and assembly
- Construction process must allow different representations
- Object construction requires many steps or parameters
- You want to improve readability of complex construction code
Avoid When:
- Object construction is simple
- Product doesn't have a complex construction process
- Immutability with many parameters can be handled by factory methods
Modern Variations:
- Fluent Builder (method chaining with return this)
- Step Builder (enforces construction order)
- Builders without Director (client controls construction)
---
5. Prototype
Intent: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.
Problem: Avoid the cost of creating objects in the standard way when it's expensive.
Solution: Implement a clone method that creates a copy of the current object.
Structure:
- Prototype (interface with clone method)
- ConcretePrototype (implements cloning)
- Client (creates new objects by cloning prototypes)
Use When:
- Classes to instantiate are specified at runtime
- Avoiding building a class hierarchy of factories parallel to product hierarchy
- Instances of a class can have only a few different state combinations
- Object creation is expensive (database, network, computation)
Avoid When:
- Simple object creation is sufficient
- Deep copying is complex (circular references, resources)
- Languages provide built-in cloning mechanisms
Considerations:
- Shallow vs. deep copy
- Cloning objects with circular references
- Handling resources (file handles, connections)
---
Structural Patterns
Structural patterns explain how to assemble objects and classes into larger structures while keeping them flexible and efficient.
6. Adapter
Intent: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise due to incompatible interfaces.
Problem: You want to use an existing class but its interface doesn't match what you need.
Solution: Create an adapter class that wraps the incompatible class and translates requests.
Structure:
- Target (interface clients use)
- Adapter (adapts Adaptee to Target)
- Adaptee (existing incompatible interface)
- Client (works with Target interface)
Variants:
- Object Adapter (uses composition)
- Class Adapter (uses inheritance, multiple inheritance required)
Use When:
- You want to use an existing class with an incompatible interface
- You want to create a reusable class that cooperates with unrelated classes
- You need to use several existing subclasses but can't adapt each one (object adapter)
- Integrating third-party libraries
Avoid When:
- You can modify the existing class directly
- The interface difference is minimal (consider wrapper functions)
---
7. Bridge
Intent: Decouple an abstraction from its implementation so the two can vary independently.
Problem: You want to avoid a permanent binding between an abstraction and its implementation, especially when both should be extensible by subclassing.
Solution: Put the abstraction and implementation in separate class hierarchies connected by composition.
Structure:
- Abstraction (defines interface, maintains reference to Implementor)
- RefinedAbstraction (extends Abstraction)
- Implementor (interface for implementation classes)
- ConcreteImplementor (concrete implementation)
Use When:
- You want to avoid permanent binding between abstraction and implementation
- Both abstractions and implementations should be extensible by subclassing
- Changes in implementation shouldn't affect clients
- You want to share implementation among multiple objects (reference counting)
- You have a proliferation of classes from a coupled interface and implementation
Avoid When:
- Only one implementation exists
- Abstraction and implementation are unlikely to change independently
- The added complexity isn't justified
Common Applications:
- GUI frameworks (abstraction: Window, implementation: WindowImpl for each OS)
- Device drivers
- Database drivers
---
8. Composite
Intent: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions uniformly.
Problem: You want to represent part-whole hierarchies and allow clients to treat objects and compositions uniformly.
Solution: Define classes for individual objects and compositions. Both implement the same interface.
Structure:
- Component (interface for objects in composition)
- Leaf (represents leaf objects, no children)
- Composite (represents composite objects, has children)
- Client (manipulates objects through Component interface)
Use When:
- You want to represent part-whole hierarchies
- You want clients to ignore the difference between individual and composite objects
- Tree structures are natural for your domain (file systems, UI components, organizational charts)
Avoid When:
- Components are too different for a common interface
- Operations differ significantly between leaves and composites
- Performance of iterating through structures is critical
Considerations:
- Where to define child management operations (Component vs. Composite)
- Component caching
- Parent references for traversal
- Ordering of children
---
9. Decorator
Intent: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
Problem: You want to add responsibilities to individual objects, not entire classes, and you want to be able to add/remove responsibilities dynamically.
Solution: Wrap the object with decorator objects that add new behaviors. Decorators implement the same interface as the wrapped object.
Structure:
- Component (interface for objects that can have responsibilities added)
- ConcreteComponent (object to which responsibilities can be attached)
- Decorator (maintains reference to Component, conforms to Component interface)
- ConcreteDecorator (adds responsibilities)
Use When:
- You want to add responsibilities to individual objects dynamically and transparently
- You want responsibilities to be withdrawable
- Extension by subclassing is impractical (many independent extensions possible)
- You want to avoid class explosion from multiple combinations
Avoid When:
- Simple subclassing is sufficient
- Order of decorators matters and becomes confusing
- Identity comparisons are critical (decorators change object identity)
Common Applications:
- I/O streams (BufferedReader, GZipInputStream)
- UI component enhancement (scrolling, borders)
- Middleware/filters
---
10. Facade
Intent: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
Problem: A subsystem has many interdependent classes with complex interfaces, making it hard to use.
Solution: Create a facade class that provides a simple interface to the complex subsystem.
Structure:
- Facade (simple interface to subsystem)
- Subsystem classes (implement functionality, handle work assigned by Facade)
- Clients (use Facade instead of subsystem directly)
Use When:
- You want to provide a simple interface to a complex subsystem
- There are many dependencies between clients and implementation classes
- You want to layer your subsystem
- You want to reduce coupling between subsystem and clients
Avoid When:
- The subsystem is already simple
- Clients need fine-grained control
- The facade becomes a monolithic god object
Considerations:
- Facade doesn't prevent access to subsystem if needed
- Multiple facades for different client needs
- Abstract facade for subsystem independence
---
11. Flyweight
Intent: Use sharing to support large numbers of fine-grained objects efficiently.
Problem: You need a large number of objects, and the cost of creating and storing them is prohibitive.
Solution: Share common parts of object state between multiple objects instead of storing all state in each object.
Structure:
- Flyweight (interface for flyweights to receive and act on extrinsic state)
- ConcreteFlyweight (implements Flyweight, stores intrinsic state)
- FlyweightFactory (creates and manages flyweight objects)
- Client (maintains references to flyweights, computes/stores extrinsic state)
Key Concepts:
- Intrinsic state: shared, stored in flyweight
- Extrinsic state: varies, computed or stored by client
Use When:
- Application uses large numbers of objects
- Storage costs are high due to sheer quantity
- Most object state can be made extrinsic
- Many groups of objects can be replaced by relatively few shared objects
- Application doesn't depend on object identity
Avoid When:
- Few objects exist
- Extrinsic state is expensive to compute/store
- Sharing introduces unacceptable complexity
Common Applications:
- Text editors (character objects)
- Game engines (particles, tiles)
- UI toolkits (widgets)
---
12. Proxy
Intent: Provide a surrogate or placeholder for another object to control access to it.
Problem: You need to add functionality when accessing an object (lazy loading, access control, caching, etc.).
Solution: Create a proxy class with the same interface as the real object. The proxy controls access and may add additional behavior.
Structure:
- Subject (interface for RealSubject and Proxy)
- RealSubject (real object that proxy represents)
- Proxy (maintains reference to RealSubject, controls access)
Types:
- Remote Proxy: represents object in different address space
- Virtual Proxy: creates expensive objects on demand (lazy initialization)
- Protection Proxy: controls access based on permissions
- Smart Reference: performs additional actions when object is accessed (reference counting, locking, loading on first access)
- Cache Proxy: caches results of expensive operations
- Logging Proxy: logs requests before forwarding
Use When:
- You need lazy initialization (virtual proxy)
- You need access control (protection proxy)
- You need to reference remote object locally (remote proxy)
- You need to add functionality before/after object access
- You want to count references or lock access (smart reference)
Avoid When:
- Direct access overhead is unacceptable
- Proxy logic becomes as complex as the real object
- Simple delegation is sufficient
Considerations:
- Proxy and RealSubject should implement same interface
- Proxy may cache RealSubject reference
- Transparent vs. explicit proxy usage
---
Behavioral Patterns
Behavioral patterns are concerned with algorithms and the assignment of responsibilities between objects.
13. Chain of Responsibility
Intent: Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along until an object handles it.
Problem: You want to send a request to one of several objects without specifying the receiver explicitly.
Solution: Create a chain of handler objects. Each handler decides whether to process the request or pass it to the next handler.
Structure:
- Handler (interface for handling requests, optional successor link)
- ConcreteHandler (handles requests it's responsible for, passes others)
- Client (initiates request to a ConcreteHandler in the chain)
Use When:
- More than one object may handle a request, handler isn't known a priori
- You want to issue request to several objects without specifying receiver explicitly
- Set of handlers should be specified dynamically
- You want to avoid tight coupling between sender and receivers
Avoid When:
- Every request must be handled (no guarantee in chain)
- Chain is too long (performance)
- Request handling path should be explicit
Common Applications:
- Event handling (UI events bubble up)
- Middleware pipelines (HTTP request processing)
- Logging frameworks (log level filtering)
- Exception handling
Variations:
- Pure chain (one handler processes request)
- Collaborative chain (multiple handlers process parts)
---
14. Command
Intent: Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.
Problem: You want to parameterize objects with operations, queue operations, log operations, or support undo.
Solution: Encapsulate requests as objects. A command object contains all information needed to perform an action or trigger an event.
Structure:
- Command (interface for executing operations)
- ConcreteCommand (implements Command, defines binding between Receiver and action)
- Receiver (knows how to perform the operation)
- Invoker (asks command to carry out request)
- Client (creates ConcreteCommand and sets Receiver)
Use When:
- You want to parameterize objects with an action to perform
- You want to specify, queue, and execute requests at different times
- You want to support undo/redo
- You want to support logging changes for crash recovery
- You want to structure system around high-level operations built on primitive operations (transactions)
Avoid When:
- Simple callbacks or function pointers suffice
- Command objects become too numerous
- State required for undo is too large
Common Applications:
- GUI buttons and menu items
- Transaction systems
- Macro recording
- Undo/redo functionality
- Job queues and schedulers
---
15. Interpreter
Intent: Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.
Problem: You need to interpret sentences in a language or notation.
Solution: Represent each grammar rule as a class. Build an abstract syntax tree of the sentence and interpret it.
Structure:
- AbstractExpression (interface for interpret operation)
- TerminalExpression (implements interpret for terminal symbols)
- NonterminalExpression (implements interpret for non-terminal symbols)
- Context (contains global information for interpreter)
- Client (builds abstract syntax tree, invokes interpret)
Use When:
- Grammar is simple (complex grammars are hard to maintain)
- Efficiency is not critical
- You want to represent grammar rules as classes
- You need to interpret custom languages or expressions
Avoid When:
- Grammar is complex (use parser generators)
- Performance is critical
- Language changes frequently
Common Applications:
- Expression evaluators
- Query languages
- Configuration file parsers
- Simple scripting languages
- Regular expression engines
---
16. Iterator
Intent: Provide a way to access elements of an aggregate object sequentially without exposing its underlying representation.
Problem: You need to traverse a collection without exposing its internal structure.
Solution: Define an iterator object that encapsulates iteration logic. The iterator knows how to traverse the collection.
Structure:
- Iterator (interface for accessing and traversing elements)
- ConcreteIterator (implements Iterator, tracks current position)
- Aggregate (interface for creating Iterator)
- ConcreteAggregate (implements Iterator creation, returns ConcreteIterator)
Use When:
- You want to access collection contents without exposing internal structure
- You want to support multiple traversals of collections
- You want to provide uniform interface for traversing different structures
- You want to separate collection from traversal logic
Avoid When:
- Simple array access is sufficient
- Collection is simple and won't change
- Language provides built-in iteration (for-each loops)
Variations:
- Internal iterator (iterator controls iteration)
- External iterator (client controls iteration)
- Robust iterator (handles collection modifications during iteration)
Modern Context: Most modern languages provide built-in iterator support (Python generators, JavaScript iterators, C# IEnumerable, Java Iterable).
---
17. Mediator
Intent: Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly.
Problem: Objects need to communicate but direct connections create tight coupling and complexity.
Solution: Centralize complex communications and control between related objects in a mediator object.
Structure:
- Mediator (interface for communicating with Colleague objects)
- ConcreteMediator (implements cooperative behavior, knows and maintains colleagues)
- Colleague (each Colleague knows its Mediator, communicates via it)
Use When:
- Set of objects communicate in well-defined but complex ways
- Reusing object is difficult due to dependencies on many others
- Behavior distributed between classes should be customizable without subclassing
- You want to reduce coupling between components
Avoid When:
- Few objects interact simply
- Mediator becomes too complex (god object)
- Direct communication is clearer
Common Applications:
- GUI dialog coordination
- Chat rooms (users communicate through room)
- Air traffic control
- Event buses and message brokers
Considerations:
- Mediator can become complex
- Can reduce subclassing of colleagues
- May create single point of failure
---
18. Memento
Intent: Without violating encapsulation, capture and externalize an object's internal state so the object can be restored to this state later.
Problem: You need to save and restore object state while maintaining encapsulation.
Solution: Use a memento object to store snapshots of another object's state. Only the originating object can read/write the memento's state.
Structure:
- Memento (stores internal state of Originator, protects against access by others)
- Originator (creates memento containing snapshot, uses memento to restore state)
- Caretaker (responsible for memento's safekeeping, never examines or operates on contents)
Use When:
- A snapshot of object's state must be saved for later restoration
- Direct interface to obtain state would expose implementation and break encapsulation
- You want to implement undo/redo
- You need checkpointing and rollback
Avoid When:
- State is large (memory/performance concerns)
- State changes are infrequent
- Encapsulation is not a concern
Considerations:
- Narrow vs. wide interface (originator has wide access, others narrow)
- Cost of saving state
- Managing mementos (caretaker responsibility)
Common Applications:
- Undo/redo functionality
- Transaction rollback
- Game save states
- Editor snapshots
---
19. Observer
Intent: Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Problem: You need to maintain consistency between related objects without tight coupling.
Solution: Define a subscription mechanism where subjects notify observers of changes.
Structure:
- Subject (knows its observers, provides interface for attaching/detaching)
- Observer (interface for objects that should be notified)
- ConcreteSubject (stores state, sends notifications)
- ConcreteObserver (maintains reference to ConcreteSubject, implements update)
Use When:
- Abstraction has two aspects, one dependent on the other
- Change to one object requires changing others, number of objects unknown
- Object should notify others without assumptions about who they are
- You want to implement event handling systems
Avoid When:
- Updates are very frequent (performance)
- Update logic is complex
- Observers and subjects create circular dependencies
Common Applications:
- Event handling systems
- Model-View-Controller (MVC)
- Data binding
- Publish-subscribe systems
- Reactive programming
Variations:
- Push model (subject sends detailed information)
- Pull model (subject sends minimal notification, observers pull data)
- Event channels (observers subscribe to specific event types)
Considerations:
- Who triggers update (explicit vs. implicit)
- Update propagation cycles
- Memory leaks (observers not unregistering)
---
20. State
Intent: Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
Problem: An object's behavior depends on its state, and it must change behavior at runtime depending on state.
Solution: Create state objects for each possible state. Delegate state-specific behavior to the current state object.
Structure:
- Context (maintains current state, delegates requests to state object)
- State (interface for encapsulating behavior associated with state)
- ConcreteState (each implements behavior for a particular state)
Use When:
- Object behavior depends on state and must change at runtime
- Operations have large conditional statements dependent on state
- State-specific behavior should be independently defined
- State transitions are well-defined
Avoid When:
- State changes are rare
- Few states exist with simple transitions
- Conditional logic is straightforward
Common Applications:
- TCP connection states
- Order processing (pending, confirmed, shipped, delivered)
- Document workflows (draft, review, approved, published)
- Game character states (idle, walking, jumping, attacking)
- UI component states (enabled, disabled, focused)
Variations:
- Context maintains current state vs. states handle transitions
- Shared state objects (flyweight) vs. unique instances
- State creation (pre-created vs. on-demand)
State vs. Strategy:
- State: behavior varies with object's state, state objects may know about each other
- Strategy: client chooses strategy, strategies are independent
---
21. Strategy
Intent: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.
Problem: You want to use different variants of an algorithm, or you want to switch algorithms at runtime.
Solution: Define a family of algorithms as separate classes implementing a common interface. Client can choose which algorithm to use.
Structure:
- Strategy (interface common to all algorithms)
- ConcreteStrategy (implements specific algorithm)
- Context (maintains reference to Strategy, uses Strategy interface)
Use When:
- Many related classes differ only in behavior
- You need different variants of an algorithm
- Algorithm uses data client shouldn't know about
- Class defines many behaviors appearing as conditional statements
Avoid When:
- Algorithms rarely change
- Clients must understand all strategies to select one
- Simple conditional is clearer
Common Applications:
- Sorting algorithms (quicksort, mergesort, heapsort)
- Compression algorithms
- Payment processing (credit card, PayPal, cryptocurrency)
- Validation rules
- Route finding algorithms
Considerations:
- Strategy and Context communicate (context passes data or passes itself)
- Strategy objects as flyweights (if stateless)
- Optional strategy (default behavior if no strategy set)
Strategy vs. State:
- Strategy: client chooses algorithm, algorithms are independent
- State: state transitions happen automatically, states may reference each other
---
22. Template Method
Intent: Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template Method lets subclasses redefine certain steps without changing the algorithm's structure.
Problem: You want to implement the invariant parts of an algorithm once and let subclasses customize variable parts.
Solution: Define an abstract class with a template method that calls abstract/hook methods. Subclasses override these methods.
Structure:
- AbstractClass (defines template method and primitive operations)
- ConcreteClass (implements primitive operations)
Template Method: Defines algorithm skeleton, calls primitive operations.
Primitive Operations:
- Abstract operations (must be implemented by subclasses)
- Concrete operations (default behavior, may be overridden)
- Hook operations (empty default, may be overridden)
Use When:
- You want to implement invariant parts of algorithm once
- Common behavior among subclasses should be factored and centralized
- You want to control which operations subclasses can extend
- You want to avoid code duplication
Avoid When:
- Algorithm doesn't have invariant structure
- Inheritance is not appropriate
- Composition would be clearer (Strategy pattern)
Common Applications:
- Framework classes (extend to customize)
- Test frameworks (setUp, tearDown hooks)
- Data processing pipelines (read, process, write)
- Lifecycle methods (initialization, execution, cleanup)
Considerations:
- Minimize primitive operations
- Naming convention for hooks (doBeforeX, afterY)
- Hollywood Principle: "Don't call us, we'll call you"
---
23. Visitor
Intent: Represent an operation to be performed on elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.
Problem: You need to perform operations on elements of a complex object structure, and you want to avoid polluting element classes with these operations.
Solution: Create a visitor class hierarchy for operations. Elements accept visitors and call the appropriate visitor method.
Structure:
- Visitor (interface declaring visit operation for each ConcreteElement)
- ConcreteVisitor (implements operations for each ConcreteElement)
- Element (interface defining accept operation)
- ConcreteElement (implements accept to call visitor)
- ObjectStructure (can enumerate elements, may provide high-level interface)
Use When:
- Object structure contains many classes with differing interfaces
- Many distinct and unrelated operations need to be performed on objects
- Classes defining object structure rarely change, but you add operations often
- Algorithm should work across several classes
Avoid When:
- Object structure classes change frequently (visitor interface must change)
- Elements are simple and uniform
- Operations are closely tied to element classes
Common Applications:
- Compilers (AST traversal for optimization, code generation, type checking)
- File system operations (calculate size, search, backup)
- Export to different formats (XML, JSON, PDF)
- Reporting and analytics
Considerations:
- Breaking encapsulation (visitor may need access to element internals)
- Adding new ConcreteElement classes is hard (all visitors must change)
- Adding new operations is easy (just add new visitor)
- Accumulating state (where to store results)
- Double dispatch (element type and visitor type determine operation)
Visitor vs. Iterator:
- Visitor: performs operation on each element
- Iterator: provides access to elements
---
Pattern Relationships
Patterns That Work Together
- Abstract Factory + Singleton: Factory instances are often singletons
- Abstract Factory + Bridge: Abstract factory can create and configure a particular bridge
- Abstract Factory + Prototype: Implement using prototype (clone prototypes rather than subclass)
- Builder + Composite: Builder can build composite structures
- Composite + Iterator: Use to iterate over composite structures
- Composite + Visitor: Visitor to perform operations on composite structures
- Decorator + Strategy: Decorator lets you change skin, Strategy lets you change guts
- Facade + Singleton: Facade object is often a singleton
- Flyweight + Composite: Shared leaf nodes in composite
- Flyweight + State/Strategy: State and strategy objects are often flyweights
- Iterator + Composite: Traverse composite structures
- Mediator + Observer: Mediator uses observer to notify colleagues
- Memento + Command: Commands use mementos for undo
- Memento + Iterator: Iterator can use memento to capture iteration state
- Observer + Mediator: Mediator can implement as observer pattern
- Prototype + Composite: Clone complex composite structures
- Proxy + Decorator: Similar structure, different intent
Pattern Alternatives
- Factory Method vs. Abstract Factory: Factory Method is simpler, Abstract Factory for families
- Decorator vs. Proxy: Decorator adds responsibilities, Proxy controls access
- Strategy vs. State: Strategy chooses algorithm, State changes behavior with state
- Template Method vs. Strategy: Template Method uses inheritance, Strategy uses composition
- Visitor vs. Iterator: Visitor performs operations, Iterator provides access
---
Anti-Patterns and Pitfalls
Singleton Abuse
- Overuse creates global state
- Makes testing difficult
- Hides dependencies
- Better: Use dependency injection
Deep Inheritance Hierarchies
- Factory Method, Template Method can lead to many subclasses
- Better: Prefer composition, use Strategy instead of Template Method
God Objects
- Mediator, Facade can become too complex
- Better: Split into smaller mediators/facades, use multiple patterns
Premature Abstraction
- Applying patterns before they're needed
- Better: Refactor toward patterns when need arises (YAGNI principle)
Over-Engineering
- Using complex patterns for simple problems
- Better: Start simple, add patterns as complexity demands
Breaking Encapsulation
- Visitor may require exposing internals
- Better: Carefully design element interfaces
---
Modern Adaptations
Dependency Injection
- Evolution of Factory patterns
- Frameworks handle object creation and wiring
Reactive Programming
- Evolution of Observer pattern
- Streams, observables, reactive extensions
Functional Programming
- Strategy → Higher-order functions
- Command → Function objects/closures
- Chain of Responsibility → Function composition
- Template Method → Higher-order functions with callbacks
Middleware/Pipeline
- Evolution of Chain of Responsibility
- HTTP middleware, message processing pipelines
Event Sourcing
- Evolution of Memento + Command
- Store all changes as events
CQRS (Command Query Responsibility Segregation)
- Uses Command pattern
- Separates read and write models
---
Selecting the Right Pattern
Ask These Questions
What problem am I solving?
- Object creation → Creational patterns
- Object composition → Structural patterns
- Object interaction → Behavioral patterns
Do I need flexibility in object creation?
- Runtime type selection → Factory Method, Abstract Factory
- Complex construction → Builder
- Cloning expensive objects → Prototype
- Single instance → Singleton
Do I need flexibility in object structure?
- Interface adaptation → Adapter
- Decouple abstraction from implementation → Bridge
- Part-whole hierarchies → Composite
- Add responsibilities dynamically → Decorator
- Simplify complex interface → Facade
- Share to reduce memory → Flyweight
- Control access → Proxy
Do I need flexibility in object behavior?
- Pass request through chain → Chain of Responsibility
- Encapsulate requests → Command
- Language/grammar interpretation → Interpreter
- Access elements sequentially → Iterator
- Centralize communication → Mediator
- Save/restore state → Memento
- Notify dependents of changes → Observer
- Vary behavior by state → State
- Interchangeable algorithms → Strategy
- Vary algorithm steps → Template Method
- Operations on object structure → Visitor
What's my priority?
- Loose coupling → Mediator, Observer, Chain of Responsibility
- Encapsulation → Memento, Iterator
- Flexibility → Strategy, State, Builder
- Simplicity → Facade, Adapter
- Performance → Flyweight, Proxy (caching)
- Extensibility → Visitor, Decorator, Chain of Responsibility
---
This reference provides a foundation for implementing any GoF pattern. Consult this when you need detailed information about pattern intent, structure, or usage guidelines.
Structural Design Patterns
Structural patterns explain how to assemble objects and classes into larger structures while keeping them flexible and efficient. These patterns help you compose classes and objects into larger structures, and explain how to tie these pieces together to form new functionality.
1. Adapter - Make Incompatible Interfaces Work Together
Intent: Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise due to incompatible interfaces.
When to Use:
- Want to use an existing class but its interface doesn't match what you need
- Want to create a reusable class that cooperates with unrelated classes
- Need to use several existing subclasses but can't adapt each one individually
- Integrating third-party libraries with incompatible interfaces
When NOT to Use:
- You can modify the existing class directly
- The interface difference is minimal (wrapper function is simpler)
Implementation Considerations:
- Object Adapter (composition): wraps adaptee, implements target interface
- Class Adapter (inheritance): inherits from adaptee, implements target interface
- Object adapter generally preferred (more flexible, supports inheritance chains)
- Bidirectional adapter (both directions of conversion)
Example Use Cases:
- Legacy system integration (adapt old API to new)
- Third-party library integration (adapt to application interface)
- Cross-platform support (adapt platform-specific APIs)
- Device driver adapters
- Data source adapters (database, REST API, file system)
---
2. Bridge - Decouple Abstraction from Implementation
Intent: Decouple an abstraction from its implementation so the two can vary independently.
When to Use:
- Avoid permanent binding between abstraction and implementation
- Both abstractions and implementations should be extensible by subclassing
- Changes in implementation shouldn't affect clients
- Share implementation among multiple objects
- Avoid class explosion from coupled abstraction/implementation hierarchies
When NOT to Use:
- Only one implementation exists
- Abstraction and implementation are unlikely to change independently
- Added complexity isn't justified
Implementation Considerations:
- Separate class hierarchies: one for abstraction, one for implementation
- Abstraction maintains reference to Implementor (composition)
- RefinedAbstraction extends Abstraction
- ConcreteImplementor provides concrete implementations
- Clients work through Abstraction, not Implementor
Example Use Cases:
- UI frameworks (abstraction: Window, implementation: WindowImpl per OS)
- Device drivers (abstraction: Device, implementation: DriverA, DriverB)
- Database drivers (abstraction: Connection, implementation: PostgreSQL, MySQL, Oracle)
- Rendering engines (abstraction: Shape, implementation: OpenGL, DirectX, Canvas)
- Payment systems (abstraction: PaymentProcessor, implementation: Stripe, PayPal)
---
3. Composite - Treat Individual Objects and Compositions Uniformly
Intent: Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions uniformly.
When to Use:
- Represent part-whole hierarchies (tree structures)
- Want clients to ignore difference between individual and composite objects
- Tree structures are natural for your domain
When NOT to Use:
- Components are too different for a common interface
- Operations differ significantly between leaves and composites
- Performance of iterating through structures is critical
Implementation Considerations:
- Component: interface for objects in composition
- Leaf: represents leaf objects, has no children
- Composite: represents composite objects, has children collection
- Where to define child management (Component vs. Composite level)
- Component caching strategies
- Parent references for traversal
- Child ordering requirements
Example Use Cases:
- File systems (files and directories)
- UI components (containers and controls)
- Organizational charts
- Graphic drawing systems
- Menu systems (menus and menu items)
- Expression trees (arithmetic operations)
---
4. Decorator - Add Responsibilities Dynamically Without Subclassing
Intent: Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
When to Use:
- Add responsibilities to individual objects dynamically and transparently
- Responsibilities should be withdrawable
- Extension by subclassing is impractical (many independent extensions possible)
- Avoid class explosion from multiple combinations of extensions
When NOT to Use:
- Simple subclassing is sufficient
- Order of decorators matters and becomes confusing
- Identity comparisons are critical (decorators change object identity)
Implementation Considerations:
- Component: interface for objects that can have responsibilities added
- Decorator: maintains reference to Component, conforms to Component interface
- ConcreteDecorator: adds specific responsibilities
- Decorators wrapping decorators (composition chain)
- Wrapper vs. wrapper with modifications
Example Use Cases:
- I/O streams (BufferedReader wrapping FileReader, GZipInputStream wrapping stream)
- UI component enhancement (scrolling, borders, tooltips, drag-and-drop)
- Middleware/filters (authentication, logging, compression)
- Text formatting (bold, italic, underline combinations)
- Feature flags (progressively decorate functionality)
---
5. Facade - Provide Simplified Interface to Complex Subsystem
Intent: Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.
When to Use:
- Provide simple interface to complex subsystem
- Many dependencies between clients and implementation classes
- Want to layer your subsystem
- Want to reduce coupling between subsystem and clients
- Simplify API for common use cases
When NOT to Use:
- The subsystem is already simple
- Clients need fine-grained control
- Facade becomes a monolithic god object
Implementation Considerations:
- Facade object provides simple interface
- Subsystem classes remain independent and unchanged
- Facade doesn't prevent direct subsystem access if needed
- Multiple facades for different client needs
- Abstract facade for subsystem independence
- Avoid making facade a monolithic dumping ground
Example Use Cases:
- Framework setup (one call initializes multiple subsystems)
- Compiler phases (simplified API to lexer, parser, code generator)
- Home automation (unified interface to lighting, climate, security)
- MVC framework (unified interface to model, view, controller setup)
- Database abstraction (unified interface to connection, query, transaction layers)
---
6. Flyweight - Share Fine-Grained Objects Efficiently
Intent: Use sharing to support large numbers of fine-grained objects efficiently.
When to Use:
- Need large number of objects and cost is prohibitive
- Storage costs are high due to sheer quantity
- Most object state can be made extrinsic (passed in, not stored)
- Many groups of objects can be replaced by relatively few shared objects
- Application doesn't depend on object identity
When NOT to Use:
- Few objects exist
- Extrinsic state is expensive to compute or store
- Sharing introduces unacceptable complexity
Implementation Considerations:
- Intrinsic state: shared, stored in flyweight (immutable)
- Extrinsic state: varies, computed or stored by client
- FlyweightFactory creates and manages flyweight objects
- Thread-safe sharing (particularly in concurrent environments)
- Cache/pool of flyweights
- State reset when returning to pool
Example Use Cases:
- Text editors (character objects, font metadata shared)
- Game engines (particles, tiles, sprites with shared graphical data)
- UI toolkits (button labels, colors shared across instances)
- Graphics systems (repeated shapes with shared data)
- String interning (strings as flyweights)
---
7. Proxy - Control Access to Another Object
Intent: Provide a surrogate or placeholder for another object to control access to it.
When to Use:
- Lazy initialization (virtual proxy): create expensive objects on demand
- Access control (protection proxy): control access based on permissions
- Reference remote object (remote proxy): represents object in different address space
- Add functionality before/after object access
- Count references or lock access (smart reference)
- Cache results of expensive operations (cache proxy)
- Log requests before forwarding (logging proxy)
When NOT to Use:
- Direct access overhead is unacceptable
- Proxy logic becomes as complex as the real object
- Simple delegation is sufficient
Implementation Considerations:
- Proxy and RealSubject implement same Subject interface
- Proxy maintains reference to RealSubject (or creates on demand)
- Additional behavior before/after forwarding to RealSubject
- Transparent vs. explicit proxy usage
- Proxy can cache RealSubject reference or recreate as needed
Proxy Types:
- Virtual Proxy: Creates expensive objects on demand (lazy loading)
- Remote Proxy: Represents object in different address space (RPC, REST)
- Protection Proxy: Controls access based on permissions
- Smart Reference: Performs additional actions (reference counting, locking, loading)
- Cache Proxy: Caches expensive operation results
- Logging Proxy: Logs method calls and arguments
Example Use Cases:
- Lazy-loaded collections (proxies for database query results)
- Remote objects (proxies for network services)
- Permission-controlled objects (proxies for sensitive data)
- Image loading (proxy loads on first access)
- Database query optimization (proxy delays loading until needed)
- Synchronized access to shared resources
- Audit logging (all accesses logged through proxy)
---
Key Relationships Between Structural Patterns
- Adapter + Facade: Adapter adapts single class, Facade simplifies subsystem
- Bridge + Strategy: Similar structure, different intent
- Composite + Iterator: Use iterator to traverse composite structures
- Composite + Visitor: Use visitor to perform operations on composite structures
- Decorator + Factory: Factory creates decorated objects
- Decorator + Strategy: Decorator changes skin, Strategy changes guts
- Facade + Singleton: Facade object is often a singleton
- Flyweight + Composite: Leaf nodes in composite can be flyweights
- Proxy + Decorator: Similar structure, different intent (proxy controls, decorator adds)
Implementation Guidelines
Language-Specific Notes
Rust: Use traits for interfaces, trait objects for runtime polymorphism, leverage ownership for smart resource management in Proxy.
Python: Use duck typing for Adapter, decorators for Decorator pattern, metaclasses for customization.
C#: Use interfaces and abstract classes, properties for lazy initialization, events for observer-like patterns.
TypeScript: Use interfaces, discriminated unions, generic constraints for type safety.
Go: Use struct embedding for composition, interfaces for polymorphism, function types.
Dart: Use mixins for code reuse, factory constructors, extension methods.
General Principles
1. Prefer Composition: Most structural patterns favor composition over inheritance 2. Maintain Type Safety: Use language type systems to enforce relationships 3. Minimize Complexity: Add structure only when it simplifies the system 4. Clear Responsibilities: Each pattern component has a single, clear purpose 5. Document Relationships: Explain how pattern components interact
---
See resources/language-guide.md for detailed language-specific implementations of these patterns.