diff --git a/examples/facade_pattern/example.py b/examples/facade_pattern/example.py
new file mode 100644
index 0000000..8d11f03
--- /dev/null
+++ b/examples/facade_pattern/example.py
@@ -0,0 +1,615 @@
+"""
+Facade Pattern Example: Home Theater System
+
+This example demonstrates the Facade Pattern using a home theater system
+that has multiple complex components (projector, amplifier, DVD player, etc.).
+The facade provides simple methods for common operations (watch movie, listen to music)
+while hiding the complexity of coordinating all components.
+
+Key features:
+- Facade providing simplified interface
+- Multiple complex subsystem components
+- Common operations (watch movie, listen to music, end experience)
+- Error handling and resource management
+- Context manager support
+- Type hints and comprehensive documentation
+
+SOLID Principles Demonstrated:
+- Single Responsibility: Each component has one clear purpose
+- Open/Closed: New components can be added without modifying facade
+- Interface Segregation: Clients use simple facade interface, not complex subsystem
+- Dependency Inversion: Facade depends on abstractions (component protocols)
+"""
+
+from abc import ABC, abstractmethod
+from typing import Optional, List
+from enum import Enum
+
+
+class PowerState(Enum):
+ """Enumeration of power states for devices."""
+ ON = "on"
+ OFF = "off"
+ STANDBY = "standby"
+
+
+class DVDPlayer:
+ """
+ DVD Player subsystem component.
+
+ Manages DVD playback with complex controls and state.
+ """
+
+ def __init__(self):
+ """Initialize DVD player."""
+ self._power_state = PowerState.OFF
+ self._current_disc: Optional[str] = None
+ self._playing = False
+
+ def on(self) -> None:
+ """Power on the DVD player."""
+ if self._power_state == PowerState.OFF:
+ self._power_state = PowerState.ON
+ print("DVD Player: Powering on...")
+ print("DVD Player: Loading firmware...")
+ print("DVD Player: Ready")
+
+ def off(self) -> None:
+ """Power off the DVD player."""
+ if self._power_state == PowerState.ON:
+ if self._playing:
+ self.stop()
+ self._power_state = PowerState.OFF
+ print("DVD Player: Powering off")
+
+ def eject(self) -> None:
+ """Eject the current disc."""
+ if self._power_state == PowerState.ON:
+ if self._playing:
+ self.stop()
+ if self._current_disc:
+ print(f"DVD Player: Ejecting '{self._current_disc}'")
+ self._current_disc = None
+ else:
+ print("DVD Player: No disc to eject")
+
+ def play(self, movie: str) -> None:
+ """
+ Play a movie.
+
+ Args:
+ movie: Title of the movie to play
+ """
+ if self._power_state == PowerState.ON:
+ self._current_disc = movie
+ self._playing = True
+ print(f"DVD Player: Playing '{movie}'")
+
+ def stop(self) -> None:
+ """Stop playback."""
+ if self._playing:
+ self._playing = False
+ print("DVD Player: Stopped")
+
+ def pause(self) -> None:
+ """Pause playback."""
+ if self._playing:
+ print("DVD Player: Paused")
+
+
+class Projector:
+ """
+ Projector subsystem component.
+
+ Manages display output with various settings.
+ """
+
+ def __init__(self):
+ """Initialize projector."""
+ self._power_state = PowerState.OFF
+ self._input_source: Optional[str] = None
+
+ def on(self) -> None:
+ """Power on the projector."""
+ if self._power_state == PowerState.OFF:
+ self._power_state = PowerState.ON
+ print("Projector: Powering on (warming up lamp)...")
+ print("Projector: Ready")
+
+ def off(self) -> None:
+ """Power off the projector."""
+ if self._power_state == PowerState.ON:
+ self._power_state = PowerState.OFF
+ print("Projector: Cooling down...")
+ print("Projector: Powering off")
+
+ def set_input(self, source: str) -> None:
+ """
+ Set the input source.
+
+ Args:
+ source: Input source name (HDMI1, HDMI2, DVD, etc.)
+ """
+ if self._power_state == PowerState.ON:
+ self._input_source = source
+ print(f"Projector: Input set to {source}")
+
+ def wide_screen_mode(self) -> None:
+ """Set widescreen display mode."""
+ if self._power_state == PowerState.ON:
+ print("Projector: Setting widescreen mode (16:9)")
+
+
+class Screen:
+ """
+ Motorized screen subsystem component.
+
+ Controls the projection screen position.
+ """
+
+ def __init__(self):
+ """Initialize screen."""
+ self._is_down = False
+
+ def down(self) -> None:
+ """Lower the screen."""
+ if not self._is_down:
+ print("Screen: Lowering screen...")
+ self._is_down = True
+ print("Screen: Screen is down")
+
+ def up(self) -> None:
+ """Raise the screen."""
+ if self._is_down:
+ print("Screen: Raising screen...")
+ self._is_down = False
+ print("Screen: Screen is up")
+
+
+class Amplifier:
+ """
+ Audio amplifier subsystem component.
+
+ Manages audio routing and volume control.
+ """
+
+ def __init__(self):
+ """Initialize amplifier."""
+ self._power_state = PowerState.OFF
+ self._input_source: Optional[str] = None
+ self._volume = 0
+ self._surround_mode = False
+
+ def on(self) -> None:
+ """Power on the amplifier."""
+ if self._power_state == PowerState.OFF:
+ self._power_state = PowerState.ON
+ print("Amplifier: Powering on...")
+ print("Amplifier: Ready")
+
+ def off(self) -> None:
+ """Power off the amplifier."""
+ if self._power_state == PowerState.ON:
+ self._power_state = PowerState.OFF
+ print("Amplifier: Powering off")
+
+ def set_input(self, source: str) -> None:
+ """
+ Set the audio input source.
+
+ Args:
+ source: Input source name
+ """
+ if self._power_state == PowerState.ON:
+ self._input_source = source
+ print(f"Amplifier: Input set to {source}")
+
+ def set_volume(self, level: int) -> None:
+ """
+ Set the volume level.
+
+ Args:
+ level: Volume level (0-100)
+ """
+ if self._power_state == PowerState.ON:
+ self._volume = max(0, min(100, level))
+ print(f"Amplifier: Volume set to {self._volume}")
+
+ def set_surround_sound(self) -> None:
+ """Enable surround sound mode."""
+ if self._power_state == PowerState.ON:
+ self._surround_mode = True
+ print("Amplifier: 5.1 surround sound enabled")
+
+
+class Lights:
+ """
+ Smart lighting subsystem component.
+
+ Controls room lighting with dimming capability.
+ """
+
+ def __init__(self):
+ """Initialize lights."""
+ self._brightness = 100 # 0-100
+
+ def dim(self, level: int) -> None:
+ """
+ Dim lights to specified level.
+
+ Args:
+ level: Brightness level (0-100)
+ """
+ self._brightness = max(0, min(100, level))
+ print(f"Lights: Dimming to {self._brightness}%")
+
+ def on(self) -> None:
+ """Turn lights fully on."""
+ self._brightness = 100
+ print("Lights: Turning on to 100%")
+
+
+class StreamingDevice:
+ """
+ Streaming device subsystem component.
+
+ Provides access to streaming services.
+ """
+
+ def __init__(self):
+ """Initialize streaming device."""
+ self._power_state = PowerState.OFF
+ self._current_app: Optional[str] = None
+
+ def on(self) -> None:
+ """Power on the streaming device."""
+ if self._power_state == PowerState.OFF:
+ self._power_state = PowerState.ON
+ print("Streaming Device: Powering on...")
+ print("Streaming Device: Connecting to network...")
+ print("Streaming Device: Ready")
+
+ def off(self) -> None:
+ """Power off the streaming device."""
+ if self._power_state == PowerState.ON:
+ self._power_state = PowerState.OFF
+ print("Streaming Device: Powering off")
+
+ def launch_app(self, app: str) -> None:
+ """
+ Launch a streaming app.
+
+ Args:
+ app: App name (Netflix, Hulu, etc.)
+ """
+ if self._power_state == PowerState.ON:
+ self._current_app = app
+ print(f"Streaming Device: Launching {app}")
+
+ def play_content(self, title: str) -> None:
+ """
+ Play content.
+
+ Args:
+ title: Content title
+ """
+ if self._power_state == PowerState.ON and self._current_app:
+ print(f"Streaming Device: Playing '{title}' on {self._current_app}")
+
+
+class HomeTheaterFacade:
+ """
+ Facade providing simplified interface to home theater system.
+
+ This facade encapsulates the complexity of coordinating multiple
+ components (DVD player, projector, amplifier, lights, etc.) and
+ provides simple methods for common operations.
+
+ Demonstrates:
+ - Simplified interface to complex subsystem
+ - Coordination of multiple components
+ - Sensible defaults for common operations
+ - Error handling and resource management
+ """
+
+ def __init__(
+ self,
+ dvd_player: DVDPlayer,
+ projector: Projector,
+ screen: Screen,
+ amplifier: Amplifier,
+ lights: Lights,
+ streaming_device: StreamingDevice
+ ):
+ """
+ Initialize the home theater facade.
+
+ Args:
+ dvd_player: DVD player component
+ projector: Projector component
+ screen: Motorized screen component
+ amplifier: Audio amplifier component
+ lights: Lighting system component
+ streaming_device: Streaming device component
+ """
+ self._dvd = dvd_player
+ self._projector = projector
+ self._screen = screen
+ self._amp = amplifier
+ self._lights = lights
+ self._streaming = streaming_device
+
+ def watch_movie(self, movie: str) -> None:
+ """
+ Watch a DVD movie with optimal settings.
+
+ This method coordinates all components to create the
+ perfect movie-watching experience.
+
+ Args:
+ movie: Title of the movie to watch
+ """
+ print("\nđŦ Getting ready to watch a movie...\n")
+
+ # Set the ambiance
+ self._lights.dim(10)
+
+ # Prepare the display
+ self._screen.down()
+ self._projector.on()
+ self._projector.set_input("DVD")
+ self._projector.wide_screen_mode()
+
+ # Set up audio
+ self._amp.on()
+ self._amp.set_input("DVD")
+ self._amp.set_volume(5)
+ self._amp.set_surround_sound()
+
+ # Start the movie
+ self._dvd.on()
+ self._dvd.play(movie)
+
+ print(f"\nâ
Enjoy your movie: '{movie}'!\n")
+
+ def end_movie(self) -> None:
+ """
+ End the movie and shut down all components.
+
+ Coordinates proper shutdown sequence for all components.
+ """
+ print("\nâšī¸ Shutting down movie experience...\n")
+
+ self._dvd.stop()
+ self._dvd.eject()
+ self._dvd.off()
+
+ self._amp.off()
+
+ self._projector.off()
+ self._screen.up()
+
+ self._lights.on()
+
+ print("\nâ
Movie experience ended.\n")
+
+ def watch_streaming(self, service: str, title: str, volume: int = 7) -> None:
+ """
+ Watch streaming content.
+
+ Args:
+ service: Streaming service name (Netflix, Hulu, etc.)
+ title: Content title
+ volume: Audio volume level (0-100), defaults to 7
+ """
+ print(f"\nđē Getting ready to watch {title} on {service}...\n")
+
+ # Set the ambiance
+ self._lights.dim(15)
+
+ # Prepare the display
+ self._screen.down()
+ self._projector.on()
+ self._projector.set_input("HDMI1")
+ self._projector.wide_screen_mode()
+
+ # Set up audio
+ self._amp.on()
+ self._amp.set_input("HDMI1")
+ self._amp.set_volume(volume)
+ self._amp.set_surround_sound()
+
+ # Start streaming
+ self._streaming.on()
+ self._streaming.launch_app(service)
+ self._streaming.play_content(title)
+
+ print(f"\nâ
Enjoy '{title}' on {service}!\n")
+
+ def end_streaming(self) -> None:
+ """End streaming and shut down components."""
+ print("\nâšī¸ Ending streaming session...\n")
+
+ self._streaming.off()
+ self._amp.off()
+ self._projector.off()
+ self._screen.up()
+ self._lights.on()
+
+ print("\nâ
Streaming session ended.\n")
+
+ def listen_to_music(self, volume: int = 5) -> None:
+ """
+ Set up for music listening.
+
+ Args:
+ volume: Audio volume level (0-100), defaults to 5
+ """
+ print("\nđĩ Setting up for music...\n")
+
+ # Lights stay on for music
+ self._lights.dim(30)
+
+ # Just need amplifier for music
+ self._amp.on()
+ self._amp.set_input("MUSIC")
+ self._amp.set_volume(volume)
+ self._amp.set_surround_sound()
+
+ print("\nâ
Ready to play music!\n")
+
+ def end_music(self) -> None:
+ """End music session."""
+ print("\nâšī¸ Ending music session...\n")
+
+ self._amp.off()
+ self._lights.on()
+
+ print("\nâ
Music session ended.\n")
+
+
+def demonstrate_without_facade():
+ """Demonstrate the complexity without using facade."""
+ print("=== WITHOUT FACADE (Complex) ===\n")
+
+ # Client must manage all components directly
+ dvd = DVDPlayer()
+ projector = Projector()
+ screen = Screen()
+ amp = Amplifier()
+ lights = Lights()
+
+ print("Client manually setting up to watch a movie:\n")
+
+ # Complex setup sequence that client must remember
+ lights.dim(10)
+ screen.down()
+ projector.on()
+ projector.set_input("DVD")
+ projector.wide_screen_mode()
+ amp.on()
+ amp.set_input("DVD")
+ amp.set_volume(5)
+ amp.set_surround_sound()
+ dvd.on()
+ dvd.play("The Matrix")
+
+ print("\n(Client had to coordinate 10+ steps manually!)\n")
+
+
+def demonstrate_with_facade():
+ """Demonstrate simplified usage with facade."""
+ print("=== WITH FACADE (Simple) ===\n")
+
+ # Create components
+ dvd = DVDPlayer()
+ projector = Projector()
+ screen = Screen()
+ amp = Amplifier()
+ lights = Lights()
+ streaming = StreamingDevice()
+
+ # Create facade
+ home_theater = HomeTheaterFacade(dvd, projector, screen, amp, lights, streaming)
+
+ print("Client using facade to watch a movie:\n")
+
+ # Simple one-line setup!
+ home_theater.watch_movie("The Matrix")
+
+ print("(Facade handled all coordination automatically!)")
+
+
+def demonstrate_multiple_scenarios():
+ """Demonstrate different usage scenarios."""
+ print("=== MULTIPLE SCENARIOS ===\n")
+
+ # Setup
+ dvd = DVDPlayer()
+ projector = Projector()
+ screen = Screen()
+ amp = Amplifier()
+ lights = Lights()
+ streaming = StreamingDevice()
+
+ home_theater = HomeTheaterFacade(dvd, projector, screen, amp, lights, streaming)
+
+ # Scenario 1: Watch DVD movie
+ print("SCENARIO 1: Watch DVD Movie")
+ home_theater.watch_movie("Inception")
+ input("Press Enter to end movie...")
+ home_theater.end_movie()
+
+ # Scenario 2: Watch streaming content
+ print("\nSCENARIO 2: Watch Streaming Content")
+ home_theater.watch_streaming("Netflix", "Stranger Things", volume=8)
+ input("Press Enter to end streaming...")
+ home_theater.end_streaming()
+
+ # Scenario 3: Listen to music
+ print("\nSCENARIO 3: Listen to Music")
+ home_theater.listen_to_music(volume=6)
+ input("Press Enter to end music...")
+ home_theater.end_music()
+
+
+def demonstrate_benefits():
+ """Demonstrate the benefits of using facade."""
+ print("=== BENEFITS OF FACADE ===\n")
+
+ # Setup
+ dvd = DVDPlayer()
+ projector = Projector()
+ screen = Screen()
+ amp = Amplifier()
+ lights = Lights()
+ streaming = StreamingDevice()
+
+ home_theater = HomeTheaterFacade(dvd, projector, screen, amp, lights, streaming)
+
+ print("Benefits demonstrated:\n")
+
+ print("1. SIMPLICITY")
+ print(" Without facade: 10+ steps to watch a movie")
+ print(" With facade: 1 method call\n")
+
+ print("2. ENCAPSULATION")
+ print(" Complex component coordination hidden from client\n")
+
+ print("3. FLEXIBILITY")
+ print(" Easy to change implementation without affecting clients\n")
+
+ print("4. REUSABILITY")
+ print(" Common operations packaged as reusable methods\n")
+
+ print("5. MAINTAINABILITY")
+ print(" Changes to subsystem don't require updating all clients\n")
+
+ # Quick demo
+ home_theater.watch_movie("The Dark Knight")
+
+
+def main():
+ """Run all demonstrations."""
+ print("Facade Pattern Example: Home Theater System\n")
+ print("=" * 60)
+
+ demonstrate_without_facade()
+
+ print("\n" + "=" * 60 + "\n")
+
+ demonstrate_with_facade()
+
+ print("\n" + "=" * 60 + "\n")
+
+ # Uncomment to run interactive scenarios
+ # demonstrate_multiple_scenarios()
+
+ demonstrate_benefits()
+
+ print("\n" + "=" * 60)
+ print("\nAll demonstrations completed successfully!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/facade_pattern/theory.md b/examples/facade_pattern/theory.md
new file mode 100644
index 0000000..2178884
--- /dev/null
+++ b/examples/facade_pattern/theory.md
@@ -0,0 +1,313 @@
+# Facade Pattern
+
+## Overview
+
+The Facade Pattern is a structural design pattern that provides a simplified, unified interface to a complex subsystem. It wraps multiple complex components with a single class that provides a cleaner, more convenient API for common tasks while still allowing direct access to subsystem components when needed.
+
+## Intent
+
+- Provide a simplified interface to a complex subsystem
+- Reduce coupling between clients and subsystem components
+- Hide complexity from clients
+- Define a higher-level interface that makes the subsystem easier to use
+- Organize subsystems into layers
+
+## Problem
+
+Consider a home theater system with many components:
+
+```python
+# Without Facade - client must manage all components
+dvd_player = DVDPlayer()
+projector = Projector()
+screen = Screen()
+amplifier = Amplifier()
+lights = Lights()
+
+# To watch a movie, client needs to:
+lights.dim(10)
+screen.down()
+projector.on()
+projector.set_input(dvd_player)
+amplifier.on()
+amplifier.set_dvd(dvd_player)
+amplifier.set_volume(5)
+dvd_player.on()
+dvd_player.play(movie)
+```
+
+This approach has problems:
+- Clients must understand complex subsystem interactions
+- Changes to subsystem require updating all client code
+- Difficult to use correctly (easy to forget steps or wrong order)
+- High coupling between clients and subsystem components
+- Complex initialization sequences repeated everywhere
+
+## Solution
+
+The Facade Pattern creates a facade class that provides simple methods encapsulating complex subsystem interactions:
+
+```python
+home_theater = HomeTheaterFacade(dvd_player, projector, screen, amplifier, lights)
+home_theater.watch_movie(movie) # Simple!
+```
+
+### Key Components
+
+1. **Facade**: Provides simplified methods that delegate to subsystem components
+2. **Subsystem Classes**: Complex components that do the actual work
+3. **Client**: Uses the facade instead of subsystem components directly
+
+## Benefits
+
+- **Simplified Interface**: Easier to use than working with subsystem directly
+- **Loose Coupling**: Clients depend on facade, not subsystem components
+- **Layered Architecture**: Helps organize complex systems into layers
+- **Flexibility**: Clients can still access subsystem components if needed
+- **Single Responsibility**: Facade handles coordination, subsystems handle specifics
+- **Maintainability**: Changes to subsystem internals don't affect clients
+
+## When to Use
+
+- When you want to provide a simple interface to a complex subsystem
+- When there are many dependencies between clients and implementation classes
+- When you want to layer your subsystem (facade for each layer)
+- When you want to decouple client code from subsystem components
+- When you need to wrap a poorly designed or legacy API
+- When you want to provide different levels of abstraction
+
+## Python-Specific Considerations
+
+Python's dynamic nature makes facades particularly elegant:
+
+- **Duck Typing**: No need for formal interfaces
+- **Default Arguments**: Simplify method signatures with sensible defaults
+- **Context Managers**: Use `__enter__`/`__exit__` for resource management
+- **Properties**: Expose simple properties that hide complex operations
+- **Decorators**: Add cross-cutting concerns to facade methods
+
+### Context Manager Facade
+
+```python
+class DatabaseFacade:
+ def __enter__(self):
+ self.connect()
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.disconnect()
+
+# Usage
+with DatabaseFacade() as db:
+ db.execute_query("SELECT * FROM users")
+```
+
+## Implementation Approaches
+
+### 1. Basic Facade
+Simple class with methods delegating to subsystem.
+
+### 2. Configurable Facade
+Facade with configuration options for customization.
+
+### 3. Layered Facades
+Multiple facades providing different abstraction levels.
+
+### 4. Singleton Facade
+Single facade instance managing shared resources.
+
+## Comparison with Other Patterns
+
+- **Adapter**: Changes interface of single class vs simplifying multiple classes
+- **Proxy**: Same interface with additional behavior vs simplified interface
+- **Mediator**: Components know about mediator vs components unaware of facade
+- **Abstract Factory**: Creates objects vs provides interface to existing objects
+- **Decorator**: Adds behavior dynamically vs provides simpler interface
+
+## Example Use Cases
+
+- **Home Automation**: Facade for controlling multiple smart devices
+- **Database Access**: Simplify complex database operations
+- **Compiler**: Facade for lexer, parser, code generator
+- **Web Frameworks**: Request facade hiding HTTP parsing, routing, etc.
+- **E-commerce Checkout**: Facade for payment, inventory, shipping systems
+- **Multimedia Libraries**: Simplify audio/video encoding/decoding
+- **Operating System APIs**: High-level facade over low-level system calls
+- **Third-Party Libraries**: Wrap complex libraries with simpler interface
+
+## Anti-Patterns to Avoid
+
+- **God Object**: Don't make facade too large or handle too much
+- **Breaking Encapsulation**: Don't expose subsystem internals unnecessarily
+- **Tight Coupling**: Facade shouldn't know about client-specific logic
+- **Bypassing Facade**: Don't let clients bypass facade and access subsystem directly (unless intentional)
+- **Too Many Facades**: Don't create facades for every small subsystem
+- **Leaky Abstraction**: Don't let subsystem complexity leak through facade
+
+## Implementation Variations
+
+### 1. Simple Facade
+Basic facade with straightforward delegation.
+
+### 2. Facade with Defaults
+Provides sensible defaults to simplify common cases.
+
+### 3. Fluent Facade
+Chainable methods for fluent interface.
+
+### 4. Facade Registry
+Multiple facades registered for different subsystems.
+
+### 5. Adaptive Facade
+Facade that adapts based on configuration or runtime conditions.
+
+## Python Implementation Patterns
+
+### Basic Facade
+
+```python
+class Facade:
+ def __init__(self):
+ self._subsystem1 = Subsystem1()
+ self._subsystem2 = Subsystem2()
+
+ def operation(self):
+ self._subsystem1.operation1()
+ self._subsystem2.operation2()
+```
+
+### Context Manager Facade
+
+```python
+class Facade:
+ def __enter__(self):
+ self._setup()
+ return self
+
+ def __exit__(self, *args):
+ self._teardown()
+```
+
+### Property-Based Facade
+
+```python
+class Facade:
+ @property
+ def status(self):
+ # Hide complex status computation
+ return self._compute_complex_status()
+```
+
+### Fluent Facade
+
+```python
+class Facade:
+ def operation1(self):
+ # Do work
+ return self
+
+ def operation2(self):
+ # Do work
+ return self
+
+# Usage: facade.operation1().operation2()
+```
+
+## Testing Considerations
+
+Facades improve testability:
+
+- **Mock Subsystems**: Easy to mock subsystem components
+- **Test Facade Interface**: Test facade without testing subsystems
+- **Integration Tests**: Use facade in integration tests
+- **Stub Subsystems**: Replace complex subsystems with stubs
+- **Isolation**: Test clients in isolation from subsystem complexity
+
+## Best Practices
+
+- **Keep It Simple**: Facade should simplify, not add complexity
+- **Sensible Defaults**: Provide defaults for common use cases
+- **Progressive Disclosure**: Simple methods for common cases, advanced methods when needed
+- **Clear Naming**: Method names should clearly indicate what they do
+- **Document Subsystem Access**: If direct access allowed, document when/why
+- **Error Handling**: Handle subsystem errors gracefully
+- **Stateless When Possible**: Prefer stateless facades for simplicity
+- **Composition Over Inheritance**: Facades should compose subsystems, not inherit
+
+## Advanced Patterns
+
+### Facade + Factory
+Factory creates appropriate facade for different contexts.
+
+### Facade + Singleton
+Single facade instance managing shared resources (connection pools, etc.).
+
+### Facade + Strategy
+Facade uses different strategies for different operations.
+
+### Facade + Template Method
+Facade methods follow template method pattern for consistency.
+
+## Real-World Examples
+
+### Web Framework Request Handling
+Flask/Django request objects facade over HTTP parsing, headers, cookies, etc.
+
+### Database ORMs
+SQLAlchemy facades over SQL generation, connection management, result parsing.
+
+### Multimedia Processing
+FFmpeg library facades provide simple API over complex video/audio processing.
+
+### Cloud SDKs
+AWS boto3 facades simplify complex API calls to cloud services.
+
+## Performance Considerations
+
+- **Lazy Initialization**: Create subsystem components only when needed
+- **Caching**: Cache expensive subsystem results
+- **Resource Pooling**: Pool expensive resources (connections, threads)
+- **Batch Operations**: Combine multiple subsystem calls into batches
+- **Async Operations**: Use async/await for I/O-bound subsystem operations
+
+## Layering with Facades
+
+Facades work well in layered architectures:
+
+```
+Presentation Layer
+ â
+Business Facade â Client uses this
+ â
+Service Layer (complex subsystems)
+ â
+Data Access Layer
+```
+
+Each layer can have its own facade providing appropriate abstraction level.
+
+## Migration to Facade Pattern
+
+When refactoring existing code:
+
+1. Identify complex subsystem interactions in client code
+2. Create facade class
+3. Move subsystem initialization to facade
+4. Extract common operation sequences into facade methods
+5. Update clients to use facade instead of subsystem directly
+6. Optionally keep subsystem accessible for advanced use cases
+7. Test that facade simplifies client code
+8. Document facade API and usage examples
+
+## Facade vs Direct Access
+
+**Use Facade When:**
+- Performing common, repetitive tasks
+- You want simple, intuitive API
+- You're okay with less control/flexibility
+
+**Use Direct Subsystem Access When:**
+- Need fine-grained control
+- Performing unusual operations
+- Optimizing specific use case
+- Facade doesn't provide needed functionality
diff --git a/examples/prototype_pattern/example.py b/examples/prototype_pattern/example.py
new file mode 100644
index 0000000..3a73fc8
--- /dev/null
+++ b/examples/prototype_pattern/example.py
@@ -0,0 +1,574 @@
+"""
+Prototype Pattern Example: Game Character System
+
+This example demonstrates the Prototype Pattern using a game character system
+where creating characters involves expensive initialization (loading graphics,
+animations, sounds). Instead of recreating characters from scratch, we clone
+prototypes and customize them.
+
+Key features:
+- Prototype interface using ABC
+- Deep and shallow copying examples
+- Prototype registry for managing multiple prototypes
+- Performance comparison between creation and cloning
+- Handling of complex nested objects
+- Type hints and comprehensive documentation
+
+SOLID Principles Demonstrated:
+- Single Responsibility: Each class has one clear purpose
+- Open/Closed: New character types can be added without modifying existing code
+- Dependency Inversion: Client depends on Prototype abstraction, not concrete classes
+"""
+
+from abc import ABC, abstractmethod
+from typing import Dict, List, Optional, Any
+from dataclasses import dataclass, field
+import copy
+import time
+from enum import Enum
+
+
+class CharacterClass(Enum):
+ """Enumeration of character classes."""
+ WARRIOR = "Warrior"
+ MAGE = "Mage"
+ ARCHER = "Archer"
+ ROGUE = "Rogue"
+
+
+@dataclass
+class Equipment:
+ """
+ Represents equipment that a character can wear.
+
+ This is a nested object to demonstrate deep vs shallow copying.
+
+ Attributes:
+ weapon: Weapon name
+ armor: Armor name
+ accessories: List of accessory names
+ """
+ weapon: str
+ armor: str
+ accessories: List[str] = field(default_factory=list)
+
+ def __str__(self) -> str:
+ acc = ", ".join(self.accessories) if self.accessories else "None"
+ return f"Weapon: {self.weapon}, Armor: {self.armor}, Accessories: {acc}"
+
+
+@dataclass
+class Stats:
+ """
+ Character statistics.
+
+ Attributes:
+ health: Health points
+ mana: Mana points
+ strength: Strength attribute
+ intelligence: Intelligence attribute
+ agility: Agility attribute
+ """
+ health: int
+ mana: int
+ strength: int
+ intelligence: int
+ agility: int
+
+ def __str__(self) -> str:
+ return (f"HP: {self.health}, MP: {self.mana}, "
+ f"STR: {self.strength}, INT: {self.intelligence}, AGI: {self.agility}")
+
+
+class GameCharacter(ABC):
+ """
+ Abstract base class for game characters (Prototype interface).
+
+ This class defines the prototype interface that all concrete
+ character types must implement.
+ """
+
+ def __init__(
+ self,
+ name: str,
+ char_class: CharacterClass,
+ level: int,
+ stats: Stats,
+ equipment: Equipment
+ ):
+ """
+ Initialize a game character.
+
+ Args:
+ name: Character name
+ char_class: Character class
+ level: Character level
+ stats: Character statistics
+ equipment: Character equipment
+ """
+ self.name = name
+ self.char_class = char_class
+ self.level = level
+ self.stats = stats
+ self.equipment = equipment
+ # Simulate expensive initialization
+ self._graphics_data = self._load_graphics()
+ self._animation_data = self._load_animations()
+ self._sound_data = self._load_sounds()
+
+ def _load_graphics(self) -> Dict[str, Any]:
+ """
+ Simulate loading graphics from disk (expensive operation).
+
+ Returns:
+ Dictionary representing graphics data
+ """
+ # Simulate expensive I/O operation
+ time.sleep(0.01)
+ return {
+ "textures": f"{self.char_class.value}_textures",
+ "models": f"{self.char_class.value}_models",
+ "sprites": f"{self.char_class.value}_sprites"
+ }
+
+ def _load_animations(self) -> Dict[str, Any]:
+ """
+ Simulate loading animations (expensive operation).
+
+ Returns:
+ Dictionary representing animation data
+ """
+ # Simulate expensive I/O operation
+ time.sleep(0.01)
+ return {
+ "idle": f"{self.char_class.value}_idle_anim",
+ "walk": f"{self.char_class.value}_walk_anim",
+ "attack": f"{self.char_class.value}_attack_anim"
+ }
+
+ def _load_sounds(self) -> Dict[str, Any]:
+ """
+ Simulate loading sound effects (expensive operation).
+
+ Returns:
+ Dictionary representing sound data
+ """
+ # Simulate expensive I/O operation
+ time.sleep(0.01)
+ return {
+ "attack": f"{self.char_class.value}_attack_sound",
+ "hurt": f"{self.char_class.value}_hurt_sound",
+ "death": f"{self.char_class.value}_death_sound"
+ }
+
+ @abstractmethod
+ def clone(self) -> 'GameCharacter':
+ """
+ Clone this character (deep copy).
+
+ Returns:
+ A new instance that is a deep copy of this character
+ """
+ pass
+
+ def shallow_clone(self) -> 'GameCharacter':
+ """
+ Create a shallow copy of this character.
+
+ This is faster but shares nested objects (equipment, stats).
+ Use with caution.
+
+ Returns:
+ A new instance that is a shallow copy
+ """
+ return copy.copy(self)
+
+ def __str__(self) -> str:
+ return (f"{self.name} (Level {self.level} {self.char_class.value})\n"
+ f" Stats: {self.stats}\n"
+ f" Equipment: {self.equipment}")
+
+
+class Warrior(GameCharacter):
+ """
+ Warrior character class.
+
+ Warriors are strong melee fighters with high health and strength.
+ """
+
+ def __init__(
+ self,
+ name: str = "Warrior",
+ level: int = 1,
+ stats: Optional[Stats] = None,
+ equipment: Optional[Equipment] = None
+ ):
+ """
+ Initialize a warrior character.
+
+ Args:
+ name: Character name
+ level: Character level
+ stats: Character statistics (defaults to warrior stats)
+ equipment: Character equipment (defaults to warrior equipment)
+ """
+ if stats is None:
+ stats = Stats(health=150, mana=30, strength=20, intelligence=5, agility=10)
+ if equipment is None:
+ equipment = Equipment(weapon="Iron Sword", armor="Plate Mail", accessories=["Shield"])
+
+ super().__init__(name, CharacterClass.WARRIOR, level, stats, equipment)
+
+ def clone(self) -> 'Warrior':
+ """
+ Clone this warrior using deep copy.
+
+ Returns:
+ A new Warrior instance that is a deep copy
+ """
+ return copy.deepcopy(self)
+
+
+class Mage(GameCharacter):
+ """
+ Mage character class.
+
+ Mages are powerful spellcasters with high mana and intelligence.
+ """
+
+ def __init__(
+ self,
+ name: str = "Mage",
+ level: int = 1,
+ stats: Optional[Stats] = None,
+ equipment: Optional[Equipment] = None
+ ):
+ """
+ Initialize a mage character.
+
+ Args:
+ name: Character name
+ level: Character level
+ stats: Character statistics (defaults to mage stats)
+ equipment: Character equipment (defaults to mage equipment)
+ """
+ if stats is None:
+ stats = Stats(health=80, mana=200, strength=5, intelligence=25, agility=8)
+ if equipment is None:
+ equipment = Equipment(weapon="Magic Staff", armor="Robe", accessories=["Spell Book", "Wand"])
+
+ super().__init__(name, CharacterClass.MAGE, level, stats, equipment)
+
+ def clone(self) -> 'Mage':
+ """
+ Clone this mage using deep copy.
+
+ Returns:
+ A new Mage instance that is a deep copy
+ """
+ return copy.deepcopy(self)
+
+
+class Archer(GameCharacter):
+ """
+ Archer character class.
+
+ Archers are ranged attackers with high agility.
+ """
+
+ def __init__(
+ self,
+ name: str = "Archer",
+ level: int = 1,
+ stats: Optional[Stats] = None,
+ equipment: Optional[Equipment] = None
+ ):
+ """
+ Initialize an archer character.
+
+ Args:
+ name: Character name
+ level: Character level
+ stats: Character statistics (defaults to archer stats)
+ equipment: Character equipment (defaults to archer equipment)
+ """
+ if stats is None:
+ stats = Stats(health=100, mana=50, strength=12, intelligence=10, agility=22)
+ if equipment is None:
+ equipment = Equipment(weapon="Longbow", armor="Leather Armor", accessories=["Quiver"])
+
+ super().__init__(name, CharacterClass.ARCHER, level, stats, equipment)
+
+ def clone(self) -> 'Archer':
+ """
+ Clone this archer using deep copy.
+
+ Returns:
+ A new Archer instance that is a deep copy
+ """
+ return copy.deepcopy(self)
+
+
+class CharacterPrototypeRegistry:
+ """
+ Registry for managing character prototypes.
+
+ This class provides centralized management of character prototypes,
+ allowing clients to create new characters by cloning registered prototypes.
+
+ Demonstrates:
+ - Prototype registry pattern
+ - Factory-like interface for prototype access
+ - Centralized prototype management
+ """
+
+ def __init__(self):
+ """Initialize the prototype registry."""
+ self._prototypes: Dict[str, GameCharacter] = {}
+
+ def register_prototype(self, key: str, prototype: GameCharacter) -> None:
+ """
+ Register a prototype in the registry.
+
+ Args:
+ key: Unique identifier for the prototype
+ prototype: The prototype character to register
+ """
+ self._prototypes[key] = prototype
+
+ def unregister_prototype(self, key: str) -> None:
+ """
+ Remove a prototype from the registry.
+
+ Args:
+ key: Identifier of the prototype to remove
+
+ Raises:
+ KeyError: If the key doesn't exist in the registry
+ """
+ del self._prototypes[key]
+
+ def create_character(self, key: str, name: Optional[str] = None) -> GameCharacter:
+ """
+ Create a new character by cloning a registered prototype.
+
+ Args:
+ key: Identifier of the prototype to clone
+ name: Optional name for the new character
+
+ Returns:
+ A new character cloned from the prototype
+
+ Raises:
+ KeyError: If the key doesn't exist in the registry
+ """
+ if key not in self._prototypes:
+ raise KeyError(f"No prototype registered with key '{key}'")
+
+ character = self._prototypes[key].clone()
+ if name:
+ character.name = name
+ return character
+
+ def list_prototypes(self) -> List[str]:
+ """
+ Get list of registered prototype keys.
+
+ Returns:
+ List of prototype identifiers
+ """
+ return list(self._prototypes.keys())
+
+
+def demonstrate_basic_cloning():
+ """Demonstrate basic character cloning."""
+ print("=== Basic Character Cloning ===\n")
+
+ # Create a warrior prototype
+ warrior_proto = Warrior(name="Warrior Template", level=10)
+ warrior_proto.stats.strength = 30 # Enhanced stats for template
+ print("Original Warrior:")
+ print(warrior_proto)
+ print()
+
+ # Clone the warrior
+ warrior_clone = warrior_proto.clone()
+ warrior_clone.name = "Ragnar"
+ warrior_clone.level = 15
+ print("Cloned Warrior (customized):")
+ print(warrior_clone)
+ print()
+
+ # Verify independence
+ warrior_proto.stats.strength = 50 # Modify original
+ print("After modifying original's strength to 50:")
+ print(f"Original strength: {warrior_proto.stats.strength}")
+ print(f"Clone's strength: {warrior_clone.stats.strength} (unchanged)")
+ print()
+
+
+def demonstrate_shallow_vs_deep_copy():
+ """Demonstrate the difference between shallow and deep copying."""
+ print("=== Shallow vs Deep Copy ===\n")
+
+ # Create original mage
+ mage_original = Mage(name="Gandalf", level=20)
+ print("Original Mage:")
+ print(mage_original)
+ print()
+
+ # Shallow clone
+ mage_shallow = mage_original.shallow_clone()
+ mage_shallow.name = "Shallow Clone"
+
+ # Deep clone
+ mage_deep = mage_original.clone()
+ mage_deep.name = "Deep Clone"
+
+ # Modify original's equipment (nested object)
+ mage_original.equipment.accessories.append("Magic Ring")
+
+ print("After adding 'Magic Ring' to original's accessories:")
+ print(f"Original accessories: {mage_original.equipment.accessories}")
+ print(f"Shallow clone accessories: {mage_shallow.equipment.accessories} (shared!)")
+ print(f"Deep clone accessories: {mage_deep.equipment.accessories} (independent)")
+ print()
+
+
+def demonstrate_prototype_registry():
+ """Demonstrate using a prototype registry."""
+ print("=== Prototype Registry ===\n")
+
+ # Create registry
+ registry = CharacterPrototypeRegistry()
+
+ # Create and register prototypes
+ warrior_proto = Warrior(name="Warrior Template", level=1)
+ mage_proto = Mage(name="Mage Template", level=1)
+ archer_proto = Archer(name="Archer Template", level=1)
+
+ registry.register_prototype("warrior", warrior_proto)
+ registry.register_prototype("mage", mage_proto)
+ registry.register_prototype("archer", archer_proto)
+
+ print(f"Registered prototypes: {registry.list_prototypes()}")
+ print()
+
+ # Create characters from prototypes
+ characters = [
+ registry.create_character("warrior", "Conan"),
+ registry.create_character("warrior", "Beowulf"),
+ registry.create_character("mage", "Merlin"),
+ registry.create_character("archer", "Legolas"),
+ registry.create_character("archer", "Robin Hood")
+ ]
+
+ print("Created characters from registry:")
+ for char in characters:
+ print(f"- {char.name} ({char.char_class.value}, Level {char.level})")
+ print()
+
+
+def demonstrate_performance_comparison():
+ """Compare performance of creation vs cloning."""
+ print("=== Performance Comparison ===\n")
+
+ # Create original (expensive)
+ print("Creating original warrior (with expensive initialization)...")
+ start_time = time.time()
+ original = Warrior(name="Template")
+ creation_time = time.time() - start_time
+ print(f"Creation time: {creation_time:.4f} seconds")
+ print()
+
+ # Clone multiple times (fast)
+ print("Cloning 10 warriors from prototype...")
+ start_time = time.time()
+ clones = []
+ for i in range(10):
+ clone = original.clone()
+ clone.name = f"Warrior {i+1}"
+ clones.append(clone)
+ cloning_time = time.time() - start_time
+ print(f"Total cloning time: {cloning_time:.4f} seconds")
+ print(f"Average time per clone: {cloning_time/10:.4f} seconds")
+ print()
+
+ # Create from scratch multiple times (expensive)
+ print("Creating 10 warriors from scratch...")
+ start_time = time.time()
+ warriors = []
+ for i in range(10):
+ warrior = Warrior(name=f"Warrior {i+1}")
+ warriors.append(warrior)
+ from_scratch_time = time.time() - start_time
+ print(f"Total creation time: {from_scratch_time:.4f} seconds")
+ print(f"Average time per creation: {from_scratch_time/10:.4f} seconds")
+ print()
+
+ speedup = from_scratch_time / cloning_time
+ print(f"Speedup: {speedup:.2f}x faster using cloning")
+ print()
+
+
+def demonstrate_customization_after_cloning():
+ """Demonstrate customizing cloned characters."""
+ print("=== Customization After Cloning ===\n")
+
+ # Create base archer
+ base_archer = Archer(name="Base Archer", level=5)
+ print("Base Archer Template:")
+ print(base_archer)
+ print()
+
+ # Clone and customize for different roles
+ print("Creating specialized archers from template:\n")
+
+ # Sniper archer (high agility)
+ sniper = base_archer.clone()
+ sniper.name = "Sniper"
+ sniper.stats.agility += 10
+ sniper.equipment.weapon = "Precision Bow"
+ sniper.equipment.accessories.append("Scope")
+ print("1. Sniper Archer:")
+ print(sniper)
+ print()
+
+ # Tank archer (high health)
+ tank = base_archer.clone()
+ tank.name = "Tank Archer"
+ tank.stats.health += 50
+ tank.stats.agility -= 5
+ tank.equipment.armor = "Heavy Armor"
+ tank.equipment.accessories.append("Shield")
+ print("2. Tank Archer:")
+ print(tank)
+ print()
+
+ # Magic archer (mana-based)
+ magic = base_archer.clone()
+ magic.name = "Magic Archer"
+ magic.stats.mana += 100
+ magic.stats.intelligence += 15
+ magic.equipment.weapon = "Enchanted Bow"
+ magic.equipment.accessories.append("Magic Quiver")
+ print("3. Magic Archer:")
+ print(magic)
+ print()
+
+
+def main():
+ """Run all demonstrations."""
+ print("Prototype Pattern Example: Game Character System\n")
+
+ demonstrate_basic_cloning()
+ demonstrate_shallow_vs_deep_copy()
+ demonstrate_prototype_registry()
+ demonstrate_performance_comparison()
+ demonstrate_customization_after_cloning()
+
+ print("All demonstrations completed successfully!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/prototype_pattern/theory.md b/examples/prototype_pattern/theory.md
new file mode 100644
index 0000000..9963adc
--- /dev/null
+++ b/examples/prototype_pattern/theory.md
@@ -0,0 +1,286 @@
+# Prototype Pattern
+
+## Overview
+
+The Prototype Pattern is a creational design pattern that creates new objects by copying existing instances (prototypes) rather than creating new instances from scratch. It uses cloning to produce new objects, allowing you to avoid expensive initialization and reduce the complexity of object creation.
+
+## Intent
+
+- Create new objects by cloning existing instances
+- Avoid costly initialization when creating similar objects
+- Reduce subclassing to achieve different object configurations
+- Encapsulate object creation complexity
+- Support adding and removing objects at runtime
+
+## Problem
+
+Consider a game where you have various character types (warriors, mages, archers) with complex initialization:
+
+```python
+class Character:
+ def __init__(self, char_type: str):
+ self.type = char_type
+ # Expensive operations
+ self.load_graphics() # Load textures from disk
+ self.load_animations() # Load animation data
+ self.load_sounds() # Load sound effects
+ self.calculate_stats() # Complex stat calculations
+ self.initialize_ai() # Set up AI behavior
+```
+
+Creating new characters repeatedly performs these expensive operations every time. If you want 100 similar warriors, you perform 100 expensive initializations even though most of the data is identical.
+
+Without the Prototype pattern, you might have:
+- Repeated expensive initialization
+- Complex constructors with many parameters
+- Tight coupling between client code and concrete classes
+- Difficulty creating variations of similar objects
+
+## Solution
+
+The Prototype Pattern defines a cloning interface that allows objects to create copies of themselves. Instead of creating new instances from scratch, you clone existing "prototype" instances and modify only what's different.
+
+### Key Components
+
+1. **Prototype Interface**: Declares the cloning interface (typically a `clone()` method)
+2. **Concrete Prototype**: Implements the cloning operation
+3. **Client**: Creates new objects by asking prototypes to clone themselves
+
+## Benefits
+
+- **Performance**: Avoid expensive initialization by cloning existing objects
+- **Reduced Complexity**: Simplifies object creation when configuration is complex
+- **Dynamic Configuration**: Add/remove prototypes at runtime
+- **Reduced Subclassing**: Create variations without defining new subclasses
+- **Encapsulation**: Hide complex object creation logic
+- **Single Responsibility**: Separates object creation from business logic
+
+## When to Use
+
+- When object initialization is expensive (I/O, database, complex calculations)
+- When you need many objects that differ only slightly
+- When you want to avoid subclass explosion for different configurations
+- When object creation requires accessing databases or external resources
+- When the system should be independent of how objects are created
+- When you want to create objects without knowing their exact classes
+
+## Python-Specific Considerations
+
+Python provides built-in support for cloning through the `copy` module:
+
+- **Shallow Copy**: `copy.copy()` - Copies object but not nested objects
+- **Deep Copy**: `copy.deepcopy()` - Recursively copies entire object tree
+- **Custom Cloning**: Implement `__copy__()` and `__deepcopy__()` methods
+- **Pickling**: Use `pickle` for serialization-based cloning
+
+### Shallow vs Deep Copy
+
+```python
+import copy
+
+# Shallow copy: nested objects are referenced, not copied
+shallow = copy.copy(original)
+
+# Deep copy: entire object tree is recursively copied
+deep = copy.deepcopy(original)
+```
+
+### Custom Clone Behavior
+
+```python
+def __copy__(self):
+ # Define custom shallow copy behavior
+ return type(self)(self.data)
+
+def __deepcopy__(self, memo):
+ # Define custom deep copy behavior
+ return type(self)(copy.deepcopy(self.data, memo))
+```
+
+## Implementation Approaches
+
+### 1. Using copy module
+Simplest approach using Python's built-in `copy.deepcopy()`.
+
+### 2. Custom clone method
+Implement custom `clone()` method for more control.
+
+### 3. Prototype registry
+Maintain registry of prototypes for centralized management.
+
+### 4. Abstract base class
+Define prototype interface using ABC.
+
+## Comparison with Other Patterns
+
+- **Factory Method**: Creates objects from scratch vs cloning
+- **Abstract Factory**: Creates families of objects vs cloning prototypes
+- **Builder**: Constructs complex objects step-by-step vs cloning
+- **Singleton**: Ensures one instance vs creating many through cloning
+
+## Example Use Cases
+
+- **Game Development**: Clone game entities (characters, items, enemies)
+- **Document Processing**: Clone document templates with formatting
+- **GUI Applications**: Clone UI components with similar configurations
+- **Database Records**: Clone records with similar data
+- **Configuration Objects**: Clone configuration with minor variations
+- **3D Graphics**: Clone complex 3D models
+- **Testing**: Create test fixtures by cloning base objects
+- **Caching**: Cache expensive-to-create objects as prototypes
+
+## Anti-Patterns to Avoid
+
+- **Overuse**: Don't use prototype when simple construction suffices
+- **Mutable Shared State**: Be careful with shallow vs deep copying
+- **Circular References**: Deep copy can fail with circular references
+- **Not Handling Resources**: Clone objects with file handles, database connections carefully
+- **Ignoring Inheritance**: Ensure cloning works correctly in inheritance hierarchies
+- **Security Issues**: Be aware of cloning sensitive data
+
+## Implementation Variations
+
+### 1. Simple Prototype
+Basic implementation with clone method.
+
+### 2. Prototype Registry
+Central registry managing multiple prototypes.
+
+### 3. Prototype Manager
+Factory-like manager creating objects from prototypes.
+
+### 4. Lazy Cloning
+Clone only when necessary (copy-on-write).
+
+### 5. Cached Prototypes
+Cache frequently cloned prototypes.
+
+## Python Implementation Patterns
+
+### Using copy module
+
+```python
+import copy
+
+class Prototype:
+ def clone(self):
+ return copy.deepcopy(self)
+```
+
+### Custom Clone Method
+
+```python
+class Prototype:
+ def clone(self):
+ # Custom cloning logic
+ new_obj = type(self)()
+ new_obj.__dict__.update(self.__dict__)
+ return new_obj
+```
+
+### Prototype Protocol
+
+```python
+from typing import Protocol
+
+class Cloneable(Protocol):
+ def clone(self) -> 'Cloneable':
+ ...
+```
+
+### Prototype Registry
+
+```python
+class PrototypeRegistry:
+ def __init__(self):
+ self._prototypes = {}
+
+ def register(self, name: str, prototype):
+ self._prototypes[name] = prototype
+
+ def create(self, name: str):
+ return self._prototypes[name].clone()
+```
+
+## Testing Considerations
+
+Prototype patterns require careful testing:
+
+- **Identity Test**: Ensure cloned object is not the same instance
+- **Equality Test**: Verify cloned object has same data
+- **Independence Test**: Modifications to clone don't affect original
+- **Deep Copy Test**: Nested objects are properly cloned
+- **Resource Handling**: File handles, connections are properly handled
+- **Performance Test**: Verify cloning is actually faster than creation
+
+## Best Practices
+
+- **Choose Copy Type Carefully**: Decide between shallow and deep copy based on needs
+- **Document Copy Semantics**: Make clear what gets copied and what's shared
+- **Handle Circular References**: Use `copy.deepcopy()` which handles cycles
+- **Consider Immutability**: Immutable objects can share data safely
+- **Resource Management**: Handle resources (files, connections) in `__deepcopy__`
+- **Test Clone Independence**: Ensure clones are truly independent
+- **Use Prototype Registry**: Centralize prototype management when appropriate
+- **Version Prototypes**: Track prototype versions if they evolve
+
+## Advanced Patterns
+
+### Prototype + Factory
+Use factory to select appropriate prototype to clone.
+
+### Prototype + Builder
+Clone base object, then use builder to customize.
+
+### Prototype + Singleton
+Single prototype registry (singleton) managing all prototypes.
+
+### Prototype + Memento
+Store object states as prototypes for undo/redo.
+
+## Real-World Examples
+
+### Game Development
+Clone enemy templates with different stats and equipment.
+
+### Document Editors
+Clone document templates (letters, invoices, reports) with standard formatting.
+
+### Scientific Computing
+Clone complex simulation configurations for parameter variations.
+
+### UI Frameworks
+Clone UI widget trees for similar layouts.
+
+## Performance Considerations
+
+- **Initialization Cost**: Prototyping is beneficial when initialization is expensive
+- **Cloning Overhead**: Deep copying has overhead; use shallow copy when possible
+- **Memory Usage**: Cloning creates new objects; monitor memory
+- **Copy-on-Write**: Consider lazy cloning for large objects
+- **Object Graph Complexity**: Deep cloning complex object graphs is expensive
+- **Caching**: Cache prototypes to avoid repeated creation
+
+## Shallow vs Deep Copy Trade-offs
+
+### Shallow Copy
+- **Pros**: Fast, low memory overhead
+- **Cons**: Nested objects are shared (can cause unexpected behavior)
+- **Use when**: Objects have primitive fields or intentionally share nested objects
+
+### Deep Copy
+- **Pros**: Complete independence, no shared state
+- **Cons**: Slower, higher memory usage, potential circular reference issues
+- **Use when**: Complete object independence is required
+
+## Migration to Prototype Pattern
+
+When refactoring existing code:
+
+1. Identify expensive object creation operations
+2. Create prototype instances with common configurations
+3. Implement clone methods (using `copy.deepcopy()` or custom logic)
+4. Replace object construction with prototype cloning
+5. Create prototype registry if managing multiple prototypes
+6. Test that clones are independent and complete
+7. Measure performance improvements
diff --git a/examples/proxy_pattern/example.py b/examples/proxy_pattern/example.py
new file mode 100644
index 0000000..e7ebff6
--- /dev/null
+++ b/examples/proxy_pattern/example.py
@@ -0,0 +1,638 @@
+"""
+Proxy Pattern Example: Image Loading and Access Control
+
+This example demonstrates the Proxy Pattern with three types of proxies:
+1. Virtual Proxy - Lazy loading of images
+2. Protection Proxy - Access control for sensitive documents
+3. Cache Proxy - Caching expensive database queries
+
+Key features:
+- Multiple proxy types (Virtual, Protection, Cache)
+- Lazy initialization
+- Access control with permissions
+- Result caching
+- Performance measurement
+- Type hints and comprehensive documentation
+
+SOLID Principles Demonstrated:
+- Single Responsibility: Each proxy type has one clear purpose
+- Open/Closed: New proxy types can be added without modifying existing code
+- Liskov Substitution: Proxies are substitutable for real objects
+- Interface Segregation: Clean interfaces for each component
+- Dependency Inversion: Clients depend on abstractions (protocols)
+"""
+
+from abc import ABC, abstractmethod
+from typing import Protocol, Optional, Dict, Any, List
+from dataclasses import dataclass
+from enum import Enum
+import time
+
+
+# ============================================================================
+# VIRTUAL PROXY - Lazy Loading
+# ============================================================================
+
+
+class Image(ABC):
+ """
+ Abstract interface for images.
+
+ This ensures proxies and real images have the same interface.
+ """
+
+ @abstractmethod
+ def display(self) -> None:
+ """Display the image."""
+ pass
+
+ @abstractmethod
+ def get_info(self) -> str:
+ """Get image information."""
+ pass
+
+
+class RealImage(Image):
+ """
+ Real image that performs expensive loading operation.
+
+ This class simulates loading a large image file from disk,
+ which is an expensive operation.
+ """
+
+ def __init__(self, filename: str):
+ """
+ Initialize and load image from disk.
+
+ Args:
+ filename: Path to image file
+ """
+ self.filename = filename
+ self._load_from_disk()
+
+ def _load_from_disk(self) -> None:
+ """
+ Simulate expensive image loading operation.
+
+ In real application, this would:
+ - Read file from disk
+ - Decode image format
+ - Allocate memory for pixel data
+ - Load textures to GPU
+ """
+ print(f"[RealImage] Loading '{self.filename}' from disk...")
+ time.sleep(0.5) # Simulate expensive I/O operation
+ print(f"[RealImage] '{self.filename}' loaded successfully")
+
+ def display(self) -> None:
+ """Display the image."""
+ print(f"[RealImage] Displaying '{self.filename}'")
+
+ def get_info(self) -> str:
+ """Get image information."""
+ return f"RealImage: {self.filename}"
+
+
+class ImageProxy(Image):
+ """
+ Virtual proxy for lazy loading images.
+
+ The proxy delays creating the real image until it's actually needed.
+ This improves performance when dealing with many images that might
+ not all be displayed.
+
+ Demonstrates:
+ - Virtual proxy pattern
+ - Lazy initialization
+ - Transparent delegation
+ """
+
+ def __init__(self, filename: str):
+ """
+ Initialize proxy without loading image.
+
+ Args:
+ filename: Path to image file
+ """
+ self.filename = filename
+ self._real_image: Optional[RealImage] = None
+ print(f"[ImageProxy] Created proxy for '{self.filename}' (not loaded yet)")
+
+ def _get_real_image(self) -> RealImage:
+ """
+ Get or create the real image (lazy loading).
+
+ Returns:
+ The real image instance
+ """
+ if self._real_image is None:
+ print(f"[ImageProxy] First access to '{self.filename}', loading now...")
+ self._real_image = RealImage(self.filename)
+ return self._real_image
+
+ def display(self) -> None:
+ """Display the image (loads if not already loaded)."""
+ self._get_real_image().display()
+
+ def get_info(self) -> str:
+ """Get image information without loading the image."""
+ loaded_status = "loaded" if self._real_image else "not loaded"
+ return f"ImageProxy: {self.filename} ({loaded_status})"
+
+
+# ============================================================================
+# PROTECTION PROXY - Access Control
+# ============================================================================
+
+
+class AccessLevel(Enum):
+ """Access levels for documents."""
+ PUBLIC = 1
+ CONFIDENTIAL = 2
+ SECRET = 3
+ TOP_SECRET = 4
+
+
+@dataclass
+class User:
+ """
+ User with access level.
+
+ Attributes:
+ username: User's name
+ access_level: Maximum access level for this user
+ """
+ username: str
+ access_level: AccessLevel
+
+
+class Document(ABC):
+ """Abstract interface for documents."""
+
+ @abstractmethod
+ def read(self) -> str:
+ """Read document content."""
+ pass
+
+ @abstractmethod
+ def write(self, content: str) -> None:
+ """Write content to document."""
+ pass
+
+ @abstractmethod
+ def get_classification(self) -> AccessLevel:
+ """Get document classification level."""
+ pass
+
+
+class RealDocument(Document):
+ """
+ Real document containing sensitive information.
+
+ This class has no access control - it's the proxy's responsibility
+ to enforce security.
+ """
+
+ def __init__(self, title: str, content: str, classification: AccessLevel):
+ """
+ Initialize document.
+
+ Args:
+ title: Document title
+ content: Document content
+ classification: Security classification level
+ """
+ self.title = title
+ self._content = content
+ self.classification = classification
+
+ def read(self) -> str:
+ """Read document content."""
+ return self._content
+
+ def write(self, content: str) -> None:
+ """Write content to document."""
+ self._content = content
+ print(f"[RealDocument] Content written to '{self.title}'")
+
+ def get_classification(self) -> AccessLevel:
+ """Get document classification level."""
+ return self.classification
+
+
+class DocumentProxy(Document):
+ """
+ Protection proxy that enforces access control.
+
+ This proxy checks user permissions before allowing access
+ to the real document.
+
+ Demonstrates:
+ - Protection proxy pattern
+ - Access control
+ - Security checks before delegation
+ """
+
+ def __init__(self, real_document: RealDocument, user: User):
+ """
+ Initialize document proxy with access control.
+
+ Args:
+ real_document: The real document to protect
+ user: The user accessing the document
+ """
+ self._real_document = real_document
+ self._user = user
+
+ def _check_read_access(self) -> bool:
+ """
+ Check if user has read access.
+
+ Returns:
+ True if access granted, False otherwise
+ """
+ has_access = self._user.access_level.value >= self._real_document.classification.value
+ if not has_access:
+ print(f"[DocumentProxy] â Access DENIED for {self._user.username} "
+ f"(Level {self._user.access_level.name}) to read "
+ f"{self._real_document.classification.name} document")
+ return has_access
+
+ def _check_write_access(self) -> bool:
+ """
+ Check if user has write access.
+
+ Write access requires higher privileges than read access.
+
+ Returns:
+ True if access granted, False otherwise
+ """
+ # Write requires at least one level higher than document classification
+ required_level = min(self._real_document.classification.value + 1, 4)
+ has_access = self._user.access_level.value >= required_level
+ if not has_access:
+ print(f"[DocumentProxy] â Access DENIED for {self._user.username} "
+ f"to write to {self._real_document.classification.name} document")
+ return has_access
+
+ def read(self) -> str:
+ """
+ Read document if user has sufficient access.
+
+ Returns:
+ Document content if access granted
+
+ Raises:
+ PermissionError: If user lacks read access
+ """
+ if self._check_read_access():
+ print(f"[DocumentProxy] â
Access GRANTED for {self._user.username} to read")
+ return self._real_document.read()
+ else:
+ raise PermissionError(f"User {self._user.username} cannot read this document")
+
+ def write(self, content: str) -> None:
+ """
+ Write to document if user has sufficient access.
+
+ Args:
+ content: Content to write
+
+ Raises:
+ PermissionError: If user lacks write access
+ """
+ if self._check_write_access():
+ print(f"[DocumentProxy] â
Access GRANTED for {self._user.username} to write")
+ self._real_document.write(content)
+ else:
+ raise PermissionError(f"User {self._user.username} cannot write to this document")
+
+ def get_classification(self) -> AccessLevel:
+ """Get document classification level."""
+ return self._real_document.get_classification()
+
+
+# ============================================================================
+# CACHE PROXY - Result Caching
+# ============================================================================
+
+
+class DatabaseQuery(Protocol):
+ """Protocol for database query objects."""
+
+ def execute(self, query: str) -> List[Dict[str, Any]]:
+ """Execute a database query."""
+ ...
+
+
+class RealDatabase:
+ """
+ Real database that performs expensive queries.
+
+ Simulates a database with slow query execution.
+ """
+
+ def __init__(self, name: str):
+ """
+ Initialize database.
+
+ Args:
+ name: Database name
+ """
+ self.name = name
+ self._data = self._initialize_data()
+
+ def _initialize_data(self) -> List[Dict[str, Any]]:
+ """Initialize sample data."""
+ return [
+ {"id": 1, "name": "Alice", "department": "Engineering"},
+ {"id": 2, "name": "Bob", "department": "Sales"},
+ {"id": 3, "name": "Charlie", "department": "Engineering"},
+ {"id": 4, "name": "Diana", "department": "Marketing"},
+ {"id": 5, "name": "Eve", "department": "Engineering"},
+ ]
+
+ def execute(self, query: str) -> List[Dict[str, Any]]:
+ """
+ Execute a database query (slow operation).
+
+ Args:
+ query: SQL-like query string
+
+ Returns:
+ Query results
+ """
+ print(f"[RealDatabase] Executing query: {query}")
+ time.sleep(0.3) # Simulate slow database query
+
+ # Simple query parsing (just for demonstration)
+ if "Engineering" in query:
+ results = [row for row in self._data if row["department"] == "Engineering"]
+ elif "Sales" in query:
+ results = [row for row in self._data if row["department"] == "Sales"]
+ else:
+ results = self._data
+
+ print(f"[RealDatabase] Query returned {len(results)} results")
+ return results
+
+
+class CachingDatabaseProxy:
+ """
+ Cache proxy that caches database query results.
+
+ This proxy caches query results to avoid repeated expensive
+ database operations for the same queries.
+
+ Demonstrates:
+ - Cache proxy pattern
+ - Result caching
+ - Performance optimization
+ """
+
+ def __init__(self, real_database: RealDatabase):
+ """
+ Initialize caching proxy.
+
+ Args:
+ real_database: The real database to proxy
+ """
+ self._real_database = real_database
+ self._cache: Dict[str, List[Dict[str, Any]]] = {}
+ self._cache_hits = 0
+ self._cache_misses = 0
+
+ def execute(self, query: str) -> List[Dict[str, Any]]:
+ """
+ Execute query with caching.
+
+ Args:
+ query: SQL-like query string
+
+ Returns:
+ Query results (from cache or database)
+ """
+ if query in self._cache:
+ self._cache_hits += 1
+ print(f"[CachingProxy] đ¯ Cache HIT for query: {query}")
+ return self._cache[query]
+ else:
+ self._cache_misses += 1
+ print(f"[CachingProxy] â Cache MISS for query: {query}")
+ results = self._real_database.execute(query)
+ self._cache[query] = results
+ return results
+
+ def get_cache_stats(self) -> Dict[str, int]:
+ """
+ Get cache statistics.
+
+ Returns:
+ Dictionary with cache hit/miss statistics
+ """
+ total = self._cache_hits + self._cache_misses
+ hit_rate = (self._cache_hits / total * 100) if total > 0 else 0
+ return {
+ "hits": self._cache_hits,
+ "misses": self._cache_misses,
+ "hit_rate": hit_rate,
+ "cached_queries": len(self._cache)
+ }
+
+ def clear_cache(self) -> None:
+ """Clear the cache."""
+ self._cache.clear()
+ print("[CachingProxy] Cache cleared")
+
+
+# ============================================================================
+# DEMONSTRATIONS
+# ============================================================================
+
+
+def demonstrate_virtual_proxy():
+ """Demonstrate lazy loading with virtual proxy."""
+ print("=== VIRTUAL PROXY (Lazy Loading) ===\n")
+
+ # Create proxies for multiple images (fast)
+ print("Creating image proxies (no loading yet):")
+ images = [
+ ImageProxy("photo1.jpg"),
+ ImageProxy("photo2.jpg"),
+ ImageProxy("photo3.jpg")
+ ]
+ print()
+
+ # Display info without loading
+ print("Getting info (without loading):")
+ for img in images:
+ print(f" {img.get_info()}")
+ print()
+
+ # Display only first image (only this one gets loaded)
+ print("Displaying only first image:")
+ images[0].display()
+ print()
+
+ # Display first image again (already loaded, no loading delay)
+ print("Displaying first image again (already loaded):")
+ images[0].display()
+ print()
+
+ # Check info again
+ print("Info after partial loading:")
+ for img in images:
+ print(f" {img.get_info()}")
+ print()
+
+
+def demonstrate_protection_proxy():
+ """Demonstrate access control with protection proxy."""
+ print("=== PROTECTION PROXY (Access Control) ===\n")
+
+ # Create users with different access levels
+ public_user = User("john_public", AccessLevel.PUBLIC)
+ confidential_user = User("jane_confidential", AccessLevel.CONFIDENTIAL)
+ secret_user = User("admin_secret", AccessLevel.SECRET)
+ top_secret_user = User("director", AccessLevel.TOP_SECRET)
+
+ # Create documents with different classifications
+ public_doc = RealDocument(
+ "Company Newsletter",
+ "Welcome to our monthly newsletter...",
+ AccessLevel.PUBLIC
+ )
+
+ secret_doc = RealDocument(
+ "Project Plans",
+ "Top secret project information...",
+ AccessLevel.SECRET
+ )
+
+ print("1. Public user trying to read PUBLIC document:")
+ proxy = DocumentProxy(public_doc, public_user)
+ try:
+ content = proxy.read()
+ print(f" Content: {content[:30]}...\n")
+ except PermissionError as e:
+ print(f" Error: {e}\n")
+
+ print("2. Public user trying to read SECRET document:")
+ proxy = DocumentProxy(secret_doc, public_user)
+ try:
+ content = proxy.read()
+ print(f" Content: {content}\n")
+ except PermissionError as e:
+ print(f" Error: {e}\n")
+
+ print("3. Secret user trying to read SECRET document:")
+ proxy = DocumentProxy(secret_doc, secret_user)
+ try:
+ content = proxy.read()
+ print(f" Content: {content[:30]}...\n")
+ except PermissionError as e:
+ print(f" Error: {e}\n")
+
+ print("4. Secret user trying to WRITE to SECRET document:")
+ proxy = DocumentProxy(secret_doc, secret_user)
+ try:
+ proxy.write("Updated secret content")
+ print()
+ except PermissionError as e:
+ print(f" Error: {e}\n")
+
+ print("5. Top Secret user trying to WRITE to SECRET document:")
+ proxy = DocumentProxy(secret_doc, top_secret_user)
+ try:
+ proxy.write("Updated secret content")
+ print()
+ except PermissionError as e:
+ print(f" Error: {e}\n")
+
+
+def demonstrate_cache_proxy():
+ """Demonstrate result caching with cache proxy."""
+ print("=== CACHE PROXY (Result Caching) ===\n")
+
+ # Create database and caching proxy
+ db = RealDatabase("EmployeeDB")
+ cache_proxy = CachingDatabaseProxy(db)
+
+ # Execute same query multiple times
+ print("Executing query for first time:")
+ results1 = cache_proxy.execute("SELECT * FROM employees WHERE department='Engineering'")
+ print(f"Results: {len(results1)} employees\n")
+
+ print("Executing SAME query again:")
+ results2 = cache_proxy.execute("SELECT * FROM employees WHERE department='Engineering'")
+ print(f"Results: {len(results2)} employees\n")
+
+ print("Executing SAME query third time:")
+ results3 = cache_proxy.execute("SELECT * FROM employees WHERE department='Engineering'")
+ print(f"Results: {len(results3)} employees\n")
+
+ print("Executing DIFFERENT query:")
+ results4 = cache_proxy.execute("SELECT * FROM employees WHERE department='Sales'")
+ print(f"Results: {len(results4)} employees\n")
+
+ print("Executing first query again:")
+ results5 = cache_proxy.execute("SELECT * FROM employees WHERE department='Engineering'")
+ print(f"Results: {len(results5)} employees\n")
+
+ # Show cache statistics
+ stats = cache_proxy.get_cache_stats()
+ print("Cache Statistics:")
+ print(f" Total queries: {stats['hits'] + stats['misses']}")
+ print(f" Cache hits: {stats['hits']}")
+ print(f" Cache misses: {stats['misses']}")
+ print(f" Hit rate: {stats['hit_rate']:.1f}%")
+ print(f" Cached queries: {stats['cached_queries']}")
+ print()
+
+
+def demonstrate_performance_comparison():
+ """Compare performance with and without proxies."""
+ print("=== PERFORMANCE COMPARISON ===\n")
+
+ print("1. Virtual Proxy Performance:\n")
+
+ # Without proxy - all images loaded immediately
+ print("Without proxy (loading 5 images immediately):")
+ start = time.time()
+ real_images = [RealImage(f"photo{i}.jpg") for i in range(5)]
+ load_time = time.time() - start
+ print(f"Total time: {load_time:.3f}s\n")
+
+ # With proxy - images loaded only when accessed
+ print("With proxy (creating 5 proxies, accessing only 1):")
+ start = time.time()
+ proxy_images = [ImageProxy(f"photo{i}.jpg") for i in range(5)]
+ proxy_images[0].display() # Only load one
+ proxy_time = time.time() - start
+ print(f"Total time: {proxy_time:.3f}s\n")
+
+ print(f"Speedup: {load_time/proxy_time:.2f}x faster with proxy\n")
+
+
+def main():
+ """Run all demonstrations."""
+ print("Proxy Pattern Example: Multiple Proxy Types\n")
+ print("=" * 60 + "\n")
+
+ demonstrate_virtual_proxy()
+ print("=" * 60 + "\n")
+
+ demonstrate_protection_proxy()
+ print("=" * 60 + "\n")
+
+ demonstrate_cache_proxy()
+ print("=" * 60 + "\n")
+
+ demonstrate_performance_comparison()
+ print("=" * 60)
+
+ print("\nAll demonstrations completed successfully!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/proxy_pattern/theory.md b/examples/proxy_pattern/theory.md
new file mode 100644
index 0000000..4c7166d
--- /dev/null
+++ b/examples/proxy_pattern/theory.md
@@ -0,0 +1,374 @@
+# Proxy Pattern
+
+## Overview
+
+The Proxy Pattern is a structural design pattern that provides a surrogate or placeholder object to control access to another object. The proxy has the same interface as the real object and controls access by performing additional operations before or after forwarding requests to the real object.
+
+## Intent
+
+- Provide a surrogate or placeholder for another object
+- Control access to the real object
+- Add additional behavior without modifying the real object
+- Lazy initialization (create expensive objects only when needed)
+- Access control (restrict access based on permissions)
+- Remote proxy (represent objects in different address spaces)
+- Smart reference (additional actions when object is accessed)
+
+## Problem
+
+Consider an image viewer application that displays high-resolution images:
+
+```python
+class Image:
+ def __init__(self, filename):
+ self.filename = filename
+ self.load_from_disk() # Expensive operation!
+
+ def display(self):
+ print(f"Displaying {self.filename}")
+
+# Problem: Loading all images immediately is slow
+images = [Image("photo1.jpg"), Image("photo2.jpg"), Image("photo3.jpg")]
+# All images loaded even if not displayed!
+```
+
+Problems without proxy:
+- Expensive operations performed immediately (even if not needed)
+- No control over access to the object
+- Cannot add cross-cutting concerns (logging, caching, access control) without modifying original class
+- Resource-intensive objects created unnecessarily
+
+## Solution
+
+The Proxy Pattern creates a proxy class that controls access to the real object:
+
+```python
+class ImageProxy:
+ def __init__(self, filename):
+ self.filename = filename
+ self._real_image = None # Not loaded yet!
+
+ def display(self):
+ if self._real_image is None:
+ self._real_image = RealImage(self.filename) # Lazy load
+ self._real_image.display()
+
+# Images only loaded when actually displayed
+images = [ImageProxy("photo1.jpg"), ImageProxy("photo2.jpg")]
+```
+
+### Key Components
+
+1. **Subject Interface**: Common interface for RealSubject and Proxy
+2. **RealSubject**: The actual object that does real work
+3. **Proxy**: Controls access to RealSubject, maintains reference to it
+
+## Types of Proxies
+
+### 1. Virtual Proxy (Lazy Loading)
+Creates expensive objects only when needed.
+
+### 2. Protection Proxy (Access Control)
+Controls access based on permissions.
+
+### 3. Remote Proxy
+Represents objects in different address spaces (network, different process).
+
+### 4. Smart Reference
+Performs additional actions (reference counting, logging, caching).
+
+### 5. Cache Proxy
+Caches results to avoid repeated expensive operations.
+
+### 6. Logging Proxy
+Adds logging to method calls.
+
+## Benefits
+
+- **Lazy Initialization**: Defer expensive object creation
+- **Access Control**: Restrict access to sensitive operations
+- **Smart Reference**: Add reference counting, locking, etc.
+- **Remote Access**: Transparently access remote objects
+- **Performance**: Cache results, batch operations
+- **Single Responsibility**: Proxy handles cross-cutting concerns
+- **Open/Closed**: Add behavior without modifying real object
+
+## When to Use
+
+- When you need lazy initialization of expensive objects
+- When you need to control access to an object
+- When you need to perform actions before/after accessing an object
+- When you need to represent remote objects locally
+- When you need reference counting or garbage collection
+- When you need to cache expensive operation results
+- When you need to log or monitor object access
+
+## Python-Specific Considerations
+
+Python provides several mechanisms for implementing proxies:
+
+### `__getattr__` and `__getattribute__`
+Intercept attribute access.
+
+```python
+class Proxy:
+ def __getattr__(self, name):
+ # Forward to real object
+ return getattr(self._real_object, name)
+```
+
+### Descriptors
+Control attribute access at class level.
+
+### Decorators
+Wrap functions with proxy behavior.
+
+### Context Managers
+Manage resources with `__enter__`/`__exit__`.
+
+### Property Decorators
+Create smart properties with lazy loading.
+
+```python
+class Image:
+ @property
+ def data(self):
+ if self._data is None:
+ self._data = self._load()
+ return self._data
+```
+
+## Implementation Approaches
+
+### 1. Explicit Proxy
+Proxy explicitly implements interface methods.
+
+### 2. Delegation Proxy
+Proxy uses `__getattr__` for automatic delegation.
+
+### 3. Inheritance Proxy
+Proxy inherits from real class (less common, tighter coupling).
+
+### 4. Decorator-Based Proxy
+Use decorators to wrap functions.
+
+## Comparison with Other Patterns
+
+- **Adapter**: Changes interface vs same interface with control
+- **Decorator**: Adds behavior vs controls access
+- **Facade**: Simplifies interface vs same interface
+- **Proxy**: Controls access, may not contain real object vs Decorator always wraps object
+
+## Example Use Cases
+
+- **Image Loading**: Lazy load images only when displayed (virtual proxy)
+- **Database Connections**: Pool connections, add connection logic (smart reference)
+- **API Rate Limiting**: Control API call frequency (protection proxy)
+- **Caching**: Cache expensive computation results (cache proxy)
+- **Access Control**: Restrict access based on user permissions (protection proxy)
+- **Remote Objects**: RPC, web services (remote proxy)
+- **Logging**: Log all method calls (logging proxy)
+- **Copy-on-Write**: Share data until modification (smart reference)
+
+## Anti-Patterns to Avoid
+
+- **Over-Proxying**: Too many proxy layers causing complexity
+- **Leaky Abstraction**: Proxy exposing implementation details
+- **Tight Coupling**: Proxy too tightly coupled to real object internals
+- **Identity Issues**: Proxy not handling identity (`is`, `==`) correctly
+- **Performance Overhead**: Proxy overhead exceeding benefits
+- **Breaking Liskov Substitution**: Proxy not properly substitutable
+
+## Implementation Variations
+
+### 1. Virtual Proxy
+Lazy initialization of expensive objects.
+
+### 2. Protection Proxy
+Access control based on user permissions.
+
+### 3. Remote Proxy
+Represents remote objects (RPC, REST).
+
+### 4. Cache Proxy
+Caches results to avoid repeated computation.
+
+### 5. Smart Reference Proxy
+Reference counting, copy-on-write, locking.
+
+### 6. Logging Proxy
+Transparently logs all method calls.
+
+## Python Implementation Patterns
+
+### Using `__getattr__`
+
+```python
+class Proxy:
+ def __init__(self, real_object):
+ self._real = real_object
+
+ def __getattr__(self, name):
+ # Delegate to real object
+ return getattr(self._real, name)
+```
+
+### Using Property Decorator
+
+```python
+class LazyProperty:
+ @property
+ def expensive_data(self):
+ if not hasattr(self, '_data'):
+ self._data = self._compute()
+ return self._data
+```
+
+### Using Descriptor
+
+```python
+class LazyDescriptor:
+ def __get__(self, obj, objtype=None):
+ if obj._value is None:
+ obj._value = obj._compute()
+ return obj._value
+```
+
+### Using Protocol
+
+```python
+from typing import Protocol
+
+class Subject(Protocol):
+ def request(self) -> str:
+ ...
+```
+
+## Testing Considerations
+
+Proxies affect testing:
+
+- **Mock Real Object**: Easy to mock real object when testing proxy
+- **Test Proxy Behavior**: Test that proxy correctly delegates
+- **Test Additional Behavior**: Test logging, caching, access control
+- **Integration Tests**: Test proxy with real object
+- **Performance Tests**: Verify proxy improves performance
+
+## Best Practices
+
+- **Same Interface**: Proxy should have same interface as real object
+- **Transparent**: Client shouldn't need to know it's using proxy
+- **Lazy Initialization**: Don't create real object in proxy constructor
+- **Exception Handling**: Proxy should handle exceptions from real object
+- **Thread Safety**: Consider thread safety for shared proxies
+- **Resource Management**: Properly clean up resources
+- **Documentation**: Document what additional behavior proxy adds
+- **Identity Semantics**: Handle `==`, `is`, `hash()` appropriately
+
+## Advanced Patterns
+
+### Proxy + Factory
+Factory creates appropriate proxy for different scenarios.
+
+### Proxy + Singleton
+Singleton proxy controlling access to singleton real object.
+
+### Proxy + Decorator
+Chain proxies to add multiple cross-cutting concerns.
+
+### Proxy + Strategy
+Proxy uses different strategies for different operations.
+
+## Real-World Examples
+
+### ORMs (SQLAlchemy, Django ORM)
+Lazy loading of related objects (virtual proxy).
+
+### Caching Libraries
+Cache proxies (Redis, Memcached clients).
+
+### Network Libraries
+Remote proxies (RPC frameworks, gRPC).
+
+### Security Frameworks
+Protection proxies (authentication, authorization).
+
+### Image Processing
+Virtual proxies for lazy image loading.
+
+## Performance Considerations
+
+- **Lazy Loading**: Balance between lazy loading overhead and memory savings
+- **Caching**: Cache expensive operations but manage cache size
+- **Network Proxies**: Minimize network round trips
+- **Thread Safety**: Locking overhead in multi-threaded scenarios
+- **Proxy Overhead**: Ensure proxy overhead is less than benefit gained
+
+## Lazy Loading Strategies
+
+### Lazy Initialization
+Load on first access.
+
+### Lazy Collection Loading
+Load collections only when accessed.
+
+### Ghost Objects
+Minimal object loaded, full object loaded on first property access.
+
+### Eager Loading
+Sometimes load eagerly to avoid N+1 problem.
+
+## Copy-on-Write with Proxy
+
+Proxy can implement copy-on-write optimization:
+
+```python
+class CopyOnWriteProxy:
+ def __init__(self, data):
+ self._data = data
+ self._copy = None
+
+ def read(self):
+ return self._copy if self._copy else self._data
+
+ def write(self, value):
+ if self._copy is None:
+ self._copy = self._data.copy()
+ self._copy[key] = value
+```
+
+## Security Considerations
+
+Protection proxies should:
+- Validate all inputs
+- Check permissions before delegating
+- Log security-relevant operations
+- Handle authentication/authorization errors
+- Not expose implementation details in error messages
+
+## Migration to Proxy Pattern
+
+When refactoring existing code:
+
+1. Identify expensive operations or access control needs
+2. Create subject interface (if not already abstract)
+3. Rename existing class to RealSubject
+4. Create Proxy class implementing subject interface
+5. Add proxy behavior (lazy loading, caching, etc.)
+6. Update clients to use proxy instead of real object
+7. Test that proxy correctly delegates to real object
+8. Measure performance improvements
+
+## Proxy vs Decorator
+
+**Proxy:**
+- Controls access to object
+- May not create real object immediately
+- Same interface
+- Usually one proxy per real object
+
+**Decorator:**
+- Adds behavior to object
+- Always wraps existing object
+- May extend interface
+- Can stack multiple decorators
diff --git a/examples/state_pattern/example.py b/examples/state_pattern/example.py
new file mode 100644
index 0000000..8a49f8a
--- /dev/null
+++ b/examples/state_pattern/example.py
@@ -0,0 +1,596 @@
+"""
+State Pattern Example: Media Player
+
+This example demonstrates the State Pattern using a media player that can be
+in different states (Stopped, Playing, Paused). Each state has different
+behavior for the same operations (play, pause, stop).
+
+Key features:
+- State interface using ABC
+- Multiple concrete states (Stopped, Playing, Paused)
+- Context class (MediaPlayer) delegating to states
+- Explicit state transitions
+- State-specific behavior
+- Entry and exit actions
+- Type hints and comprehensive documentation
+
+SOLID Principles Demonstrated:
+- Single Responsibility: Each state handles one state's behavior
+- Open/Closed: New states can be added without modifying existing code
+- Liskov Substitution: All states are substitutable through common interface
+- Interface Segregation: Clean state interface
+- Dependency Inversion: Context depends on state abstraction
+"""
+
+from abc import ABC, abstractmethod
+from typing import Optional, List
+from dataclasses import dataclass
+from enum import Enum, auto
+
+
+class PlayerEvent(Enum):
+ """Events that can occur in the media player."""
+ PLAY = auto()
+ PAUSE = auto()
+ STOP = auto()
+ NEXT = auto()
+ PREVIOUS = auto()
+
+
+@dataclass
+class Track:
+ """
+ Represents a music track.
+
+ Attributes:
+ title: Track title
+ artist: Artist name
+ duration: Duration in seconds
+ """
+ title: str
+ artist: str
+ duration: int
+
+ def __str__(self) -> str:
+ minutes = self.duration // 60
+ seconds = self.duration % 60
+ return f"{self.title} by {self.artist} ({minutes}:{seconds:02d})"
+
+
+class PlayerState(ABC):
+ """
+ Abstract base class for player states.
+
+ Each state implements different behavior for player operations.
+ States can trigger transitions to other states.
+ """
+
+ def __init__(self):
+ """Initialize state."""
+ self.name = self.__class__.__name__
+
+ def on_enter(self, player: 'MediaPlayer') -> None:
+ """
+ Called when entering this state.
+
+ Args:
+ player: The media player context
+ """
+ print(f"[State] Entering {self.name} state")
+
+ def on_exit(self, player: 'MediaPlayer') -> None:
+ """
+ Called when exiting this state.
+
+ Args:
+ player: The media player context
+ """
+ print(f"[State] Exiting {self.name} state")
+
+ @abstractmethod
+ def play(self, player: 'MediaPlayer') -> None:
+ """
+ Handle play button press.
+
+ Args:
+ player: The media player context
+ """
+ pass
+
+ @abstractmethod
+ def pause(self, player: 'MediaPlayer') -> None:
+ """
+ Handle pause button press.
+
+ Args:
+ player: The media player context
+ """
+ pass
+
+ @abstractmethod
+ def stop(self, player: 'MediaPlayer') -> None:
+ """
+ Handle stop button press.
+
+ Args:
+ player: The media player context
+ """
+ pass
+
+ def next_track(self, player: 'MediaPlayer') -> None:
+ """
+ Handle next track button press.
+
+ Default implementation works for all states.
+
+ Args:
+ player: The media player context
+ """
+ if player.has_next_track():
+ player.move_to_next_track()
+ print(f"[Player] âī¸ Next track: {player.current_track}")
+ else:
+ print("[Player] Already at last track")
+
+ def previous_track(self, player: 'MediaPlayer') -> None:
+ """
+ Handle previous track button press.
+
+ Default implementation works for all states.
+
+ Args:
+ player: The media player context
+ """
+ if player.has_previous_track():
+ player.move_to_previous_track()
+ print(f"[Player] âŽī¸ Previous track: {player.current_track}")
+ else:
+ print("[Player] Already at first track")
+
+
+class StoppedState(PlayerState):
+ """
+ State when player is stopped.
+
+ In this state:
+ - Play starts playback
+ - Pause does nothing
+ - Stop does nothing
+ """
+
+ def play(self, player: 'MediaPlayer') -> None:
+ """Start playback from current track."""
+ print(f"[Player] âļī¸ Starting playback: {player.current_track}")
+ player.set_state(PlayingState())
+
+ def pause(self, player: 'MediaPlayer') -> None:
+ """Cannot pause when stopped."""
+ print("[Player] â ī¸ Cannot pause - player is stopped")
+
+ def stop(self, player: 'MediaPlayer') -> None:
+ """Already stopped."""
+ print("[Player] â ī¸ Player is already stopped")
+
+
+class PlayingState(PlayerState):
+ """
+ State when player is playing.
+
+ In this state:
+ - Play does nothing (already playing)
+ - Pause pauses playback
+ - Stop stops playback
+ """
+
+ def on_enter(self, player: 'MediaPlayer') -> None:
+ """Start audio playback when entering playing state."""
+ super().on_enter(player)
+ print(f"[Audio] đ Playing audio: {player.current_track}")
+
+ def on_exit(self, player: 'MediaPlayer') -> None:
+ """Stop audio when exiting playing state."""
+ print("[Audio] đ Stopping audio")
+ super().on_exit(player)
+
+ def play(self, player: 'MediaPlayer') -> None:
+ """Already playing."""
+ print("[Player] â ī¸ Already playing")
+
+ def pause(self, player: 'MediaPlayer') -> None:
+ """Pause playback."""
+ print(f"[Player] â¸ī¸ Pausing: {player.current_track}")
+ player.set_state(PausedState())
+
+ def stop(self, player: 'MediaPlayer') -> None:
+ """Stop playback."""
+ print(f"[Player] âšī¸ Stopping: {player.current_track}")
+ player.set_state(StoppedState())
+
+ def next_track(self, player: 'MediaPlayer') -> None:
+ """
+ Skip to next track while playing.
+
+ Overrides default to auto-play next track.
+ """
+ if player.has_next_track():
+ # Exit current state (stop audio)
+ self.on_exit(player)
+ player.move_to_next_track()
+ print(f"[Player] âī¸ Next track: {player.current_track}")
+ # Re-enter state (start new audio)
+ self.on_enter(player)
+ else:
+ print("[Player] Already at last track")
+
+ def previous_track(self, player: 'MediaPlayer') -> None:
+ """
+ Skip to previous track while playing.
+
+ Overrides default to auto-play previous track.
+ """
+ if player.has_previous_track():
+ # Exit current state (stop audio)
+ self.on_exit(player)
+ player.move_to_previous_track()
+ print(f"[Player] âŽī¸ Previous track: {player.current_track}")
+ # Re-enter state (start new audio)
+ self.on_enter(player)
+ else:
+ print("[Player] Already at first track")
+
+
+class PausedState(PlayerState):
+ """
+ State when player is paused.
+
+ In this state:
+ - Play resumes playback
+ - Pause does nothing (already paused)
+ - Stop stops playback
+ """
+
+ def play(self, player: 'MediaPlayer') -> None:
+ """Resume playback."""
+ print(f"[Player] âļī¸ Resuming: {player.current_track}")
+ player.set_state(PlayingState())
+
+ def pause(self, player: 'MediaPlayer') -> None:
+ """Already paused."""
+ print("[Player] â ī¸ Already paused")
+
+ def stop(self, player: 'MediaPlayer') -> None:
+ """Stop playback."""
+ print(f"[Player] âšī¸ Stopping: {player.current_track}")
+ player.set_state(StoppedState())
+
+
+class MediaPlayer:
+ """
+ Media player context class.
+
+ This class maintains the current state and delegates all
+ operations to the current state object. It also manages
+ the playlist and track navigation.
+
+ Demonstrates:
+ - Context maintaining current state
+ - Delegating operations to state
+ - State transition management
+ - Separation of concerns (player manages tracks, states handle behavior)
+ """
+
+ def __init__(self, playlist: List[Track]):
+ """
+ Initialize media player.
+
+ Args:
+ playlist: List of tracks to play
+ """
+ if not playlist:
+ raise ValueError("Playlist cannot be empty")
+
+ self._playlist = playlist
+ self._current_track_index = 0
+ self._state = StoppedState()
+ self._state_history: List[str] = []
+
+ print(f"[Player] Initialized with {len(playlist)} tracks")
+ print(f"[Player] Current track: {self.current_track}")
+
+ @property
+ def current_track(self) -> Track:
+ """Get the current track."""
+ return self._playlist[self._current_track_index]
+
+ @property
+ def state_name(self) -> str:
+ """Get the name of the current state."""
+ return self._state.name
+
+ def set_state(self, state: PlayerState) -> None:
+ """
+ Set a new state.
+
+ This method handles the state transition, calling
+ on_exit on the old state and on_enter on the new state.
+
+ Args:
+ state: New state to transition to
+ """
+ self._state.on_exit(self)
+ self._state_history.append(self._state.name)
+ self._state = state
+ self._state.on_enter(self)
+
+ def has_next_track(self) -> bool:
+ """Check if there's a next track."""
+ return self._current_track_index < len(self._playlist) - 1
+
+ def has_previous_track(self) -> bool:
+ """Check if there's a previous track."""
+ return self._current_track_index > 0
+
+ def move_to_next_track(self) -> None:
+ """Move to next track."""
+ if self.has_next_track():
+ self._current_track_index += 1
+
+ def move_to_previous_track(self) -> None:
+ """Move to previous track."""
+ if self.has_previous_track():
+ self._current_track_index -= 1
+
+ # Public API - delegates to current state
+
+ def play(self) -> None:
+ """Play or resume playback."""
+ print(f"\nâļī¸ PLAY button pressed (Current state: {self.state_name})")
+ self._state.play(self)
+
+ def pause(self) -> None:
+ """Pause playback."""
+ print(f"\nâ¸ī¸ PAUSE button pressed (Current state: {self.state_name})")
+ self._state.pause(self)
+
+ def stop(self) -> None:
+ """Stop playback."""
+ print(f"\nâšī¸ STOP button pressed (Current state: {self.state_name})")
+ self._state.stop(self)
+
+ def next_track(self) -> None:
+ """Skip to next track."""
+ print(f"\nâī¸ NEXT button pressed (Current state: {self.state_name})")
+ self._state.next_track(self)
+
+ def previous_track(self) -> None:
+ """Skip to previous track."""
+ print(f"\nâŽī¸ PREVIOUS button pressed (Current state: {self.state_name})")
+ self._state.previous_track(self)
+
+ def get_state_history(self) -> List[str]:
+ """
+ Get history of state transitions.
+
+ Returns:
+ List of state names in chronological order
+ """
+ return self._state_history.copy()
+
+ def display_status(self) -> None:
+ """Display current player status."""
+ print(f"\nđ Player Status:")
+ print(f" State: {self.state_name}")
+ print(f" Track: {self.current_track}")
+ print(f" Track {self._current_track_index + 1} of {len(self._playlist)}")
+
+
+# ============================================================================
+# DEMONSTRATIONS
+# ============================================================================
+
+
+def demonstrate_basic_state_transitions():
+ """Demonstrate basic state transitions."""
+ print("=== BASIC STATE TRANSITIONS ===\n")
+
+ # Create playlist
+ playlist = [
+ Track("Bohemian Rhapsody", "Queen", 354),
+ Track("Stairway to Heaven", "Led Zeppelin", 482),
+ Track("Hotel California", "Eagles", 390)
+ ]
+
+ player = MediaPlayer(playlist)
+ player.display_status()
+
+ # Transition: Stopped -> Playing
+ player.play()
+
+ # Try to play again (already playing)
+ player.play()
+
+ # Transition: Playing -> Paused
+ player.pause()
+
+ # Transition: Paused -> Playing
+ player.play()
+
+ # Transition: Playing -> Stopped
+ player.stop()
+
+ # Try to pause when stopped
+ player.pause()
+
+ player.display_status()
+
+
+def demonstrate_track_navigation():
+ """Demonstrate track navigation in different states."""
+ print("\n=== TRACK NAVIGATION ===\n")
+
+ playlist = [
+ Track("Song 1", "Artist A", 180),
+ Track("Song 2", "Artist B", 200),
+ Track("Song 3", "Artist C", 220)
+ ]
+
+ player = MediaPlayer(playlist)
+
+ # Navigate while stopped
+ print("\n--- Navigation while STOPPED ---")
+ player.next_track()
+ player.display_status()
+
+ # Start playing
+ player.play()
+
+ # Navigate while playing (auto-continues playback)
+ print("\n--- Navigation while PLAYING ---")
+ player.next_track()
+ player.display_status()
+
+ # Try to go beyond last track
+ player.next_track()
+
+ # Go back
+ player.previous_track()
+ player.display_status()
+
+
+def demonstrate_state_history():
+ """Demonstrate tracking state history."""
+ print("\n=== STATE HISTORY ===\n")
+
+ playlist = [Track("Test Song", "Test Artist", 180)]
+ player = MediaPlayer(playlist)
+
+ # Perform various operations
+ player.play()
+ player.pause()
+ player.play()
+ player.stop()
+ player.play()
+ player.stop()
+
+ # Show state history
+ history = player.get_state_history()
+ print(f"\nđ State Transition History:")
+ for i, state in enumerate(history, 1):
+ print(f" {i}. {state}")
+ print(f" {len(history) + 1}. {player.state_name} (current)")
+
+
+def demonstrate_state_specific_behavior():
+ """Demonstrate how same action has different behavior in different states."""
+ print("\n=== STATE-SPECIFIC BEHAVIOR ===\n")
+
+ playlist = [
+ Track("Example Song", "Example Artist", 200),
+ Track("Another Song", "Another Artist", 180)
+ ]
+
+ player = MediaPlayer(playlist)
+
+ print("Testing PLAY button in different states:\n")
+
+ # Play in Stopped state -> starts playback
+ print("1. When STOPPED:")
+ player.play()
+
+ # Play in Playing state -> already playing
+ print("\n2. When PLAYING:")
+ player.play()
+
+ # Pause, then play in Paused state -> resumes
+ player.pause()
+ print("\n3. When PAUSED:")
+ player.play()
+
+ print("\n" + "=" * 50)
+ print("\nTesting PAUSE button in different states:\n")
+
+ # Stop first
+ player.stop()
+
+ # Pause in Stopped state -> cannot pause
+ print("1. When STOPPED:")
+ player.pause()
+
+ # Start playing and pause
+ player.play()
+ print("\n2. When PLAYING:")
+ player.pause()
+
+ # Pause in Paused state -> already paused
+ print("\n3. When PAUSED:")
+ player.pause()
+
+
+def demonstrate_without_state_pattern():
+ """Show what the code would look like without State pattern."""
+ print("\n=== WITHOUT STATE PATTERN (Anti-pattern) ===\n")
+
+ print("Without State Pattern, the MediaPlayer class would have:")
+ print("""
+class MediaPlayer:
+ def __init__(self, playlist):
+ self.playlist = playlist
+ self.state = "stopped" # String-based state!
+
+ def play(self):
+ if self.state == "stopped":
+ print("Starting playback")
+ self.state = "playing"
+ elif self.state == "playing":
+ print("Already playing")
+ elif self.state == "paused":
+ print("Resuming playback")
+ self.state = "playing"
+
+ def pause(self):
+ if self.state == "stopped":
+ print("Cannot pause")
+ elif self.state == "playing":
+ print("Pausing")
+ self.state = "paused"
+ elif self.state == "paused":
+ print("Already paused")
+
+ # More conditionals in every method!
+ # Hard to maintain, test, and extend!
+ """)
+
+ print("Problems:")
+ print(" â Complex conditionals in every method")
+ print(" â Adding new states requires modifying all methods")
+ print(" â State transitions are scattered and implicit")
+ print(" â Violates Open/Closed Principle")
+ print(" â Difficult to test individual state behaviors")
+ print(" â No compile-time checking of state transitions")
+
+
+def main():
+ """Run all demonstrations."""
+ print("State Pattern Example: Media Player\n")
+ print("=" * 60)
+
+ demonstrate_basic_state_transitions()
+ print("\n" + "=" * 60)
+
+ demonstrate_track_navigation()
+ print("\n" + "=" * 60)
+
+ demonstrate_state_history()
+ print("\n" + "=" * 60)
+
+ demonstrate_state_specific_behavior()
+ print("\n" + "=" * 60)
+
+ demonstrate_without_state_pattern()
+ print("\n" + "=" * 60)
+
+ print("\nAll demonstrations completed successfully!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/state_pattern/theory.md b/examples/state_pattern/theory.md
new file mode 100644
index 0000000..ecd3ee1
--- /dev/null
+++ b/examples/state_pattern/theory.md
@@ -0,0 +1,410 @@
+# State Pattern
+
+## Overview
+
+The State Pattern is a behavioral design pattern that allows an object to alter its behavior when its internal state changes. The object will appear to change its class by delegating state-specific behavior to separate state objects, making state transitions explicit and manageable.
+
+## Intent
+
+- Allow an object to alter its behavior when its internal state changes
+- Encapsulate state-specific behavior in separate classes
+- Make state transitions explicit
+- Eliminate complex conditional statements based on state
+- Make it easy to add new states without changing existing code
+
+## Problem
+
+Consider a document editor with different editing modes (view, edit, review):
+
+```python
+class Document:
+ def __init__(self):
+ self.mode = "view" # view, edit, review
+
+ def click(self):
+ if self.mode == "view":
+ print("Opening document")
+ elif self.mode == "edit":
+ print("Selecting text")
+ elif self.mode == "review":
+ print("Adding comment")
+
+ def type(self, text):
+ if self.mode == "view":
+ print("Cannot type in view mode")
+ elif self.mode == "edit":
+ print(f"Typing: {text}")
+ elif self.mode == "review":
+ print("Cannot type in review mode")
+```
+
+Problems without State pattern:
+- Complex conditional logic scattered throughout methods
+- Adding new states requires modifying multiple methods
+- State transitions are implicit and hard to track
+- Violates Open/Closed Principle
+- Difficult to maintain and test
+- State-specific behavior is not encapsulated
+
+## Solution
+
+The State Pattern encapsulates state-specific behavior in separate state classes:
+
+```python
+class DocumentState(ABC):
+ @abstractmethod
+ def click(self, document): pass
+
+ @abstractmethod
+ def type(self, document, text): pass
+
+class ViewState(DocumentState):
+ def click(self, document):
+ print("Opening document")
+ document.set_state(EditState())
+
+ def type(self, document, text):
+ print("Cannot type in view mode")
+
+class Document:
+ def __init__(self):
+ self.state = ViewState()
+
+ def click(self):
+ self.state.click(self)
+
+ def type(self, text):
+ self.state.type(self, text)
+```
+
+### Key Components
+
+1. **Context**: Maintains current state and delegates state-specific requests
+2. **State Interface**: Defines interface for state-specific behavior
+3. **Concrete States**: Implement behavior for specific states
+
+## Benefits
+
+- **Open/Closed Principle**: New states can be added without modifying existing code
+- **Single Responsibility**: Each state class handles one state's behavior
+- **Explicit State Transitions**: State changes are clear and trackable
+- **Eliminates Conditionals**: No complex if/else or switch statements
+- **Encapsulation**: State-specific behavior is encapsulated
+- **Testability**: Each state can be tested independently
+
+## When to Use
+
+- When object behavior depends on its state
+- When you have large conditional statements based on object state
+- When state transitions follow complex rules
+- When you need to add new states frequently
+- When different states have substantially different behaviors
+- When you want to make state transitions explicit
+
+## Python-Specific Considerations
+
+Python's dynamic typing simplifies state pattern implementation:
+
+- **Duck Typing**: No need for formal state interface
+- **Dynamic Method Assignment**: Can replace methods dynamically
+- **Enums**: Use Enum for state identifiers
+- **Context Managers**: States can use `__enter__`/`__exit__`
+- **Decorators**: Decorate methods to add state validation
+
+### Using Enums for State Identity
+
+```python
+from enum import Enum, auto
+
+class DocumentMode(Enum):
+ VIEW = auto()
+ EDIT = auto()
+ REVIEW = auto()
+```
+
+## Implementation Approaches
+
+### 1. Classic State Pattern
+Separate state classes implementing state interface.
+
+### 2. State Enum
+Use enum values with conditional logic (simpler but less flexible).
+
+### 3. State Dictionary
+Map states to behavior dictionaries.
+
+### 4. State Machine
+Formal state machine with transition rules.
+
+## Comparison with Other Patterns
+
+- **Strategy**: Algorithms are interchangeable vs states change object behavior
+- **Command**: Encapsulates request vs encapsulates state behavior
+- **State Machine**: State pattern is implementation of state machine
+- **Chain of Responsibility**: Request passes through chain vs state handles request
+
+## Example Use Cases
+
+- **Document Editor**: View, edit, review modes
+- **TCP Connection**: Established, listening, closed states
+- **Vending Machine**: Ready, has money, dispensing states
+- **Order Processing**: New, paid, shipped, delivered states
+- **Media Player**: Playing, paused, stopped states
+- **Game Character**: Idle, running, jumping, attacking states
+- **Authentication**: Unauthenticated, authenticated, locked states
+- **Traffic Light**: Red, yellow, green states
+
+## Anti-Patterns to Avoid
+
+- **Too Many States**: Keep state count manageable
+- **God State**: States that know too much about context internals
+- **State Proliferation**: Don't create state for every minor variation
+- **Circular Dependencies**: States shouldn't depend on each other
+- **Shared State**: State objects should be stateless or manage only state-specific data
+- **Missing Transitions**: Ensure all valid transitions are implemented
+
+## Implementation Variations
+
+### 1. Classic State Objects
+Each state is an object instance.
+
+### 2. Singleton States
+States are singletons (stateless states).
+
+### 3. State Factory
+Factory creates appropriate state objects.
+
+### 4. Hierarchical States
+States can have substates.
+
+### 5. State with Transition Rules
+Explicit transition validation.
+
+## Python Implementation Patterns
+
+### Using ABC
+
+```python
+from abc import ABC, abstractmethod
+
+class State(ABC):
+ @abstractmethod
+ def handle(self, context):
+ pass
+```
+
+### Using Protocol
+
+```python
+from typing import Protocol
+
+class State(Protocol):
+ def handle(self, context) -> None:
+ ...
+```
+
+### Using Enum
+
+```python
+from enum import Enum, auto
+
+class State(Enum):
+ IDLE = auto()
+ RUNNING = auto()
+ STOPPED = auto()
+```
+
+### State Transition Validation
+
+```python
+class State(ABC):
+ allowed_transitions: List[Type['State']] = []
+
+ def can_transition_to(self, state_class):
+ return state_class in self.allowed_transitions
+```
+
+## Testing Considerations
+
+State patterns are highly testable:
+
+- **Test Each State**: Test state behavior independently
+- **Test Transitions**: Verify valid and invalid transitions
+- **Mock Context**: Mock context for testing states
+- **State Coverage**: Ensure all states are tested
+- **Transition Coverage**: Test all state transitions
+
+## Best Practices
+
+- **Stateless States**: Prefer stateless state objects (singletons)
+- **Clear Transitions**: Make state transitions explicit and clear
+- **Transition Validation**: Validate state transitions
+- **State Interface**: Keep state interface focused and cohesive
+- **Context API**: Context should provide clean API to states
+- **Immutable Transitions**: Consider making state transitions immutable
+- **Document State Machine**: Document valid states and transitions
+- **Error Handling**: Handle invalid state transitions gracefully
+
+## Advanced Patterns
+
+### State + Factory
+Factory creates appropriate initial state.
+
+### State + Strategy
+States use different strategies for their behavior.
+
+### State + Command
+Commands trigger state transitions.
+
+### Hierarchical State Machines
+States can have nested substates.
+
+## Real-World Examples
+
+### TCP Connection States
+Linux kernel TCP stack uses state pattern.
+
+### Game Engines
+Game character AI and animation states.
+
+### Workflow Engines
+Order processing, approval workflows.
+
+### UI Frameworks
+Component states (enabled, disabled, hover, focus).
+
+## Performance Considerations
+
+- **State Object Creation**: Use singletons for stateless states
+- **Transition Overhead**: Minimize state transition overhead
+- **State Caching**: Cache frequently used states
+- **Memory**: Consider memory impact of many state objects
+- **Transition Validation**: Balance validation thoroughness with performance
+
+## State Machine Diagram
+
+State patterns implement state machines:
+
+```
+ +-------+
+ | Start |
+ +-------+
+ |
+ v
+ +---------+ event1 +---------+
+ | State A | ------------> | State B |
+ +---------+ +---------+
+ ^ |
+ | | event2
+ +-------------------------+
+```
+
+## Transition Guards
+
+Add conditions to transitions:
+
+```python
+class State(ABC):
+ def can_transition(self, context, next_state):
+ # Check if transition is allowed
+ return True
+
+ def transition(self, context, next_state):
+ if self.can_transition(context, next_state):
+ context.set_state(next_state)
+ else:
+ raise InvalidTransitionError()
+```
+
+## Entry and Exit Actions
+
+States can perform actions on entry/exit:
+
+```python
+class State(ABC):
+ def on_enter(self, context):
+ """Called when entering this state."""
+ pass
+
+ def on_exit(self, context):
+ """Called when exiting this state."""
+ pass
+```
+
+## Hierarchical States
+
+States can have substates:
+
+```python
+class ParentState(State):
+ def __init__(self):
+ self.substate = None
+
+ def handle(self, context):
+ if self.substate:
+ self.substate.handle(context)
+ else:
+ # Handle at parent level
+ pass
+```
+
+## State History
+
+Remember previous states for back/undo:
+
+```python
+class Context:
+ def __init__(self):
+ self.state = InitialState()
+ self.state_history = []
+
+ def set_state(self, state):
+ self.state_history.append(self.state)
+ self.state = state
+
+ def revert_state(self):
+ if self.state_history:
+ self.state = self.state_history.pop()
+```
+
+## Concurrent States
+
+Object can be in multiple states simultaneously:
+
+```python
+class Context:
+ def __init__(self):
+ self.states = {
+ 'connection': DisconnectedState(),
+ 'authentication': UnauthenticatedState(),
+ 'activity': IdleState()
+ }
+```
+
+## Migration to State Pattern
+
+When refactoring existing code:
+
+1. Identify state-dependent behavior
+2. Extract states as enum or constants
+3. Create state interface
+4. Create concrete state classes
+5. Move state-specific behavior to state classes
+6. Replace conditionals with state delegation
+7. Implement state transitions
+8. Test all states and transitions
+9. Document state machine
+
+## State vs Strategy
+
+**State:**
+- Changes behavior based on internal state
+- State changes during object lifetime
+- States aware of each other (for transitions)
+- Context delegates to current state
+
+**Strategy:**
+- Encapsulates algorithms
+- Strategy typically set once
+- Strategies independent of each other
+- Context chooses strategy explicitly
diff --git a/examples/template_method_pattern/example.py b/examples/template_method_pattern/example.py
new file mode 100644
index 0000000..39577de
--- /dev/null
+++ b/examples/template_method_pattern/example.py
@@ -0,0 +1,565 @@
+"""
+Template Method Pattern Example: Data Processing Pipeline
+
+This example demonstrates the Template Method Pattern using a data processing
+pipeline. Different data processors (CSV, JSON, XML) follow the same algorithm
+structure (read, validate, transform, write) but implement each step differently.
+
+Key features:
+- Abstract base class with template method
+- Multiple concrete implementations (CSV, JSON, XML processors)
+- Abstract operations (must be implemented)
+- Hook operations (optional customization)
+- Consistent algorithm structure enforced
+- Type hints and comprehensive documentation
+
+SOLID Principles Demonstrated:
+- Single Responsibility: Each processor handles one data format
+- Open/Closed: New processors can be added without modifying base class
+- Liskov Substitution: All processors are substitutable through base class
+- Dependency Inversion: Clients depend on abstract DataProcessor
+"""
+
+from abc import ABC, abstractmethod
+from typing import List, Dict, Any, Optional
+from dataclasses import dataclass
+import json
+import csv
+from io import StringIO
+
+
+@dataclass
+class DataRecord:
+ """
+ Represents a single data record.
+
+ Attributes:
+ id: Record identifier
+ name: Record name
+ value: Record value
+ metadata: Additional metadata
+ """
+ id: int
+ name: str
+ value: float
+ metadata: Dict[str, Any]
+
+ def to_dict(self) -> Dict[str, Any]:
+ """Convert record to dictionary."""
+ return {
+ "id": self.id,
+ "name": self.name,
+ "value": self.value,
+ "metadata": self.metadata
+ }
+
+
+class DataProcessor(ABC):
+ """
+ Abstract base class defining the data processing template.
+
+ This class defines the algorithm skeleton for data processing:
+ 1. Read data from source
+ 2. Validate data
+ 3. Transform data
+ 4. Write data to destination
+
+ Subclasses implement format-specific steps while the template
+ method ensures all steps are executed in the correct order.
+
+ Demonstrates:
+ - Template method pattern
+ - Algorithm skeleton with customizable steps
+ - Hook methods for optional customization
+ - Consistent processing flow
+ """
+
+ def process(self, source: str, destination: str) -> None:
+ """
+ Template method defining the data processing algorithm.
+
+ This method should NOT be overridden by subclasses.
+ It defines the invariant processing structure.
+
+ Args:
+ source: Source data location/content
+ destination: Destination for processed data
+ """
+ print(f"\n{'='*60}")
+ print(f"Starting {self.__class__.__name__} processing")
+ print(f"{'='*60}\n")
+
+ # Step 1: Read data (abstract - must be implemented)
+ print("Step 1: Reading data...")
+ raw_data = self.read_data(source)
+ print(f" â Read {len(raw_data)} bytes\n")
+
+ # Step 2: Parse data (abstract - must be implemented)
+ print("Step 2: Parsing data...")
+ records = self.parse_data(raw_data)
+ print(f" â Parsed {len(records)} records\n")
+
+ # Step 3: Validate (abstract - must be implemented)
+ print("Step 3: Validating data...")
+ valid_records = self.validate_data(records)
+ print(f" â Validated {len(valid_records)} records\n")
+
+ # Step 4: Pre-transform hook (optional)
+ print("Step 4: Pre-transform processing...")
+ self.before_transform(valid_records)
+ print(" â Pre-transform complete\n")
+
+ # Step 5: Transform (abstract - must be implemented)
+ print("Step 5: Transforming data...")
+ transformed_records = self.transform_data(valid_records)
+ print(f" â Transformed {len(transformed_records)} records\n")
+
+ # Step 6: Post-transform hook (optional)
+ print("Step 6: Post-transform processing...")
+ self.after_transform(transformed_records)
+ print(" â Post-transform complete\n")
+
+ # Step 7: Format output (abstract - must be implemented)
+ print("Step 7: Formatting output...")
+ formatted_data = self.format_output(transformed_records)
+ print(f" â Formatted {len(formatted_data)} bytes\n")
+
+ # Step 8: Write data (abstract - must be implemented)
+ print("Step 8: Writing data...")
+ self.write_data(formatted_data, destination)
+ print(" â Write complete\n")
+
+ print(f"{'='*60}")
+ print(f"Processing complete!")
+ print(f"{'='*60}\n")
+
+ # Abstract methods - must be implemented by subclasses
+
+ @abstractmethod
+ def read_data(self, source: str) -> str:
+ """
+ Read data from source.
+
+ Args:
+ source: Source location/content
+
+ Returns:
+ Raw data as string
+ """
+ pass
+
+ @abstractmethod
+ def parse_data(self, raw_data: str) -> List[Dict[str, Any]]:
+ """
+ Parse raw data into structured format.
+
+ Args:
+ raw_data: Raw data string
+
+ Returns:
+ List of parsed records
+ """
+ pass
+
+ @abstractmethod
+ def validate_data(self, records: List[Dict[str, Any]]) -> List[DataRecord]:
+ """
+ Validate and convert records.
+
+ Args:
+ records: Parsed records
+
+ Returns:
+ List of valid DataRecord objects
+ """
+ pass
+
+ @abstractmethod
+ def transform_data(self, records: List[DataRecord]) -> List[DataRecord]:
+ """
+ Transform data records.
+
+ Args:
+ records: Valid records
+
+ Returns:
+ Transformed records
+ """
+ pass
+
+ @abstractmethod
+ def format_output(self, records: List[DataRecord]) -> str:
+ """
+ Format records for output.
+
+ Args:
+ records: Transformed records
+
+ Returns:
+ Formatted output string
+ """
+ pass
+
+ @abstractmethod
+ def write_data(self, data: str, destination: str) -> None:
+ """
+ Write data to destination.
+
+ Args:
+ data: Formatted data
+ destination: Destination location
+ """
+ pass
+
+ # Hook methods - optional, have default implementations
+
+ def before_transform(self, records: List[DataRecord]) -> None:
+ """
+ Hook called before transformation.
+
+ Default implementation does nothing.
+ Subclasses can override for preprocessing.
+
+ Args:
+ records: Records to preprocess
+ """
+ pass
+
+ def after_transform(self, records: List[DataRecord]) -> None:
+ """
+ Hook called after transformation.
+
+ Default implementation does nothing.
+ Subclasses can override for postprocessing.
+
+ Args:
+ records: Transformed records
+ """
+ pass
+
+
+class CSVDataProcessor(DataProcessor):
+ """
+ CSV data processor implementation.
+
+ Processes data in CSV format following the template structure.
+ """
+
+ def read_data(self, source: str) -> str:
+ """Read CSV data."""
+ # In real implementation, would read from file
+ return source
+
+ def parse_data(self, raw_data: str) -> List[Dict[str, Any]]:
+ """Parse CSV data."""
+ records = []
+ reader = csv.DictReader(StringIO(raw_data))
+ for row in reader:
+ records.append(row)
+ return records
+
+ def validate_data(self, records: List[Dict[str, Any]]) -> List[DataRecord]:
+ """Validate CSV records."""
+ valid_records = []
+ for record in records:
+ try:
+ data_record = DataRecord(
+ id=int(record['id']),
+ name=record['name'],
+ value=float(record['value']),
+ metadata={'source': 'csv'}
+ )
+ valid_records.append(data_record)
+ except (KeyError, ValueError) as e:
+ print(f" â ī¸ Skipping invalid record: {e}")
+ return valid_records
+
+ def transform_data(self, records: List[DataRecord]) -> List[DataRecord]:
+ """Transform CSV data - apply 10% increase to values."""
+ for record in records:
+ record.value *= 1.10
+ record.metadata['transformed'] = True
+ return records
+
+ def format_output(self, records: List[DataRecord]) -> str:
+ """Format as CSV."""
+ output = StringIO()
+ if records:
+ fieldnames = ['id', 'name', 'value']
+ writer = csv.DictWriter(output, fieldnames=fieldnames)
+ writer.writeheader()
+ for record in records:
+ writer.writerow({
+ 'id': record.id,
+ 'name': record.name,
+ 'value': f"{record.value:.2f}"
+ })
+ return output.getvalue()
+
+ def write_data(self, data: str, destination: str) -> None:
+ """Write CSV data."""
+ # In real implementation, would write to file
+ print(f" Writing to {destination}:")
+ print(f" {data[:100]}..." if len(data) > 100 else f" {data}")
+
+
+class JSONDataProcessor(DataProcessor):
+ """
+ JSON data processor implementation.
+
+ Processes data in JSON format with additional logging hook.
+ """
+
+ def read_data(self, source: str) -> str:
+ """Read JSON data."""
+ return source
+
+ def parse_data(self, raw_data: str) -> List[Dict[str, Any]]:
+ """Parse JSON data."""
+ data = json.loads(raw_data)
+ return data if isinstance(data, list) else [data]
+
+ def validate_data(self, records: List[Dict[str, Any]]) -> List[DataRecord]:
+ """Validate JSON records."""
+ valid_records = []
+ for record in records:
+ try:
+ data_record = DataRecord(
+ id=record['id'],
+ name=record['name'],
+ value=float(record['value']),
+ metadata=record.get('metadata', {'source': 'json'})
+ )
+ valid_records.append(data_record)
+ except (KeyError, ValueError, TypeError) as e:
+ print(f" â ī¸ Skipping invalid record: {e}")
+ return valid_records
+
+ def transform_data(self, records: List[DataRecord]) -> List[DataRecord]:
+ """Transform JSON data - double the values."""
+ for record in records:
+ record.value *= 2.0
+ record.metadata['transformed'] = True
+ record.metadata['transformation'] = 'doubled'
+ return records
+
+ def format_output(self, records: List[DataRecord]) -> str:
+ """Format as JSON."""
+ output_data = [record.to_dict() for record in records]
+ return json.dumps(output_data, indent=2)
+
+ def write_data(self, data: str, destination: str) -> None:
+ """Write JSON data."""
+ print(f" Writing to {destination}:")
+ print(f" {data[:100]}..." if len(data) > 100 else f" {data}")
+
+ # Override hook to add logging
+ def after_transform(self, records: List[DataRecord]) -> None:
+ """Log statistics after transformation."""
+ total_value = sum(r.value for r in records)
+ avg_value = total_value / len(records) if records else 0
+ print(f" đ Stats: Total={total_value:.2f}, Average={avg_value:.2f}")
+
+
+class XMLDataProcessor(DataProcessor):
+ """
+ XML data processor implementation.
+
+ Simplified XML processing (for demonstration).
+ """
+
+ def read_data(self, source: str) -> str:
+ """Read XML data."""
+ return source
+
+ def parse_data(self, raw_data: str) -> List[Dict[str, Any]]:
+ """Parse XML data (simplified)."""
+ # Simplified parsing for demonstration
+ # In real implementation, would use xml.etree or lxml
+ records = []
+ import re
+
+ # Extract record elements
+ record_pattern = r'(.*?)'
+ record_matches = re.findall(record_pattern, raw_data, re.DOTALL)
+
+ for match in record_matches:
+ record = {}
+ # Extract fields
+ for field in ['id', 'name', 'value']:
+ field_pattern = f'<{field}>(.*?){field}>'
+ field_match = re.search(field_pattern, match)
+ if field_match:
+ record[field] = field_match.group(1)
+ if record:
+ records.append(record)
+
+ return records
+
+ def validate_data(self, records: List[Dict[str, Any]]) -> List[DataRecord]:
+ """Validate XML records."""
+ valid_records = []
+ for record in records:
+ try:
+ data_record = DataRecord(
+ id=int(record['id']),
+ name=record['name'],
+ value=float(record['value']),
+ metadata={'source': 'xml'}
+ )
+ valid_records.append(data_record)
+ except (KeyError, ValueError) as e:
+ print(f" â ī¸ Skipping invalid record: {e}")
+ return valid_records
+
+ def transform_data(self, records: List[DataRecord]) -> List[DataRecord]:
+ """Transform XML data - add 100 to values."""
+ for record in records:
+ record.value += 100.0
+ record.metadata['transformed'] = True
+ record.metadata['transformation'] = 'added_100'
+ return records
+
+ def format_output(self, records: List[DataRecord]) -> str:
+ """Format as XML."""
+ xml_parts = ['', '']
+ for record in records:
+ xml_parts.append(' ')
+ xml_parts.append(f' {record.id}')
+ xml_parts.append(f' {record.name}')
+ xml_parts.append(f' {record.value:.2f}')
+ xml_parts.append(' ')
+ xml_parts.append('')
+ return '\n'.join(xml_parts)
+
+ def write_data(self, data: str, destination: str) -> None:
+ """Write XML data."""
+ print(f" Writing to {destination}:")
+ lines = data.split('\n')
+ preview = '\n '.join(lines[:5])
+ print(f" {preview}...")
+
+ # Override both hooks for XML-specific processing
+ def before_transform(self, records: List[DataRecord]) -> None:
+ """Add XML namespace metadata."""
+ for record in records:
+ record.metadata['xmlns'] = 'http://example.com/schema'
+
+ def after_transform(self, records: List[DataRecord]) -> None:
+ """Validate XML structure."""
+ print(f" â XML validation passed for {len(records)} records")
+
+
+# ============================================================================
+# DEMONSTRATIONS
+# ============================================================================
+
+
+def demonstrate_csv_processing():
+ """Demonstrate CSV data processing."""
+ print("=== CSV DATA PROCESSING ===\n")
+
+ csv_data = """id,name,value
+1,Product A,100.50
+2,Product B,200.75
+3,Product C,150.25"""
+
+ processor = CSVDataProcessor()
+ processor.process(csv_data, "output.csv")
+
+
+def demonstrate_json_processing():
+ """Demonstrate JSON data processing."""
+ print("\n=== JSON DATA PROCESSING ===\n")
+
+ json_data = json.dumps([
+ {"id": 1, "name": "Item X", "value": 50.0, "metadata": {"category": "A"}},
+ {"id": 2, "name": "Item Y", "value": 75.5, "metadata": {"category": "B"}},
+ {"id": 3, "name": "Item Z", "value": 100.0, "metadata": {"category": "A"}}
+ ])
+
+ processor = JSONDataProcessor()
+ processor.process(json_data, "output.json")
+
+
+def demonstrate_xml_processing():
+ """Demonstrate XML data processing."""
+ print("\n=== XML DATA PROCESSING ===\n")
+
+ xml_data = """
+
+
+ 1
+ Widget A
+ 25.00
+
+
+ 2
+ Widget B
+ 35.50
+
+"""
+
+ processor = XMLDataProcessor()
+ processor.process(xml_data, "output.xml")
+
+
+def demonstrate_polymorphic_processing():
+ """Demonstrate polymorphic processing of different formats."""
+ print("\n=== POLYMORPHIC PROCESSING ===\n")
+
+ # Different processors, same interface
+ processors = [
+ (CSVDataProcessor(), "id,name,value\n1,Test,100", "test.csv"),
+ (JSONDataProcessor(), '[{"id": 1, "name": "Test", "value": 100}]', "test.json"),
+ ]
+
+ print("Processing multiple formats using same template:\n")
+ for processor, data, output in processors:
+ print(f"Using {processor.__class__.__name__}:")
+ processor.process(data, output)
+ print()
+
+
+def demonstrate_template_benefits():
+ """Demonstrate benefits of template method pattern."""
+ print("\n=== TEMPLATE METHOD BENEFITS ===\n")
+
+ print("Benefits demonstrated:\n")
+
+ print("1. CONSISTENT STRUCTURE")
+ print(" All processors follow the same 8-step pipeline")
+ print(" Read -> Parse -> Validate -> Transform -> Write\n")
+
+ print("2. CODE REUSE")
+ print(" Common algorithm structure defined once in base class")
+ print(" Subclasses only implement format-specific details\n")
+
+ print("3. EASY TO EXTEND")
+ print(" New formats can be added by implementing abstract methods")
+ print(" No changes to existing processors or template\n")
+
+ print("4. HOOKS FOR CUSTOMIZATION")
+ print(" before_transform() and after_transform() hooks")
+ print(" Optional - can override when needed\n")
+
+ print("5. ENFORCED PROCESS")
+ print(" Template method guarantees steps execute in correct order")
+ print(" Subclasses cannot skip or reorder steps\n")
+
+
+def main():
+ """Run all demonstrations."""
+ print("Template Method Pattern Example: Data Processing Pipeline\n")
+
+ demonstrate_csv_processing()
+ demonstrate_json_processing()
+ demonstrate_xml_processing()
+ demonstrate_polymorphic_processing()
+ demonstrate_template_benefits()
+
+ print("\nAll demonstrations completed successfully!")
+
+
+if __name__ == "__main__":
+ main()
diff --git a/examples/template_method_pattern/theory.md b/examples/template_method_pattern/theory.md
new file mode 100644
index 0000000..8246d63
--- /dev/null
+++ b/examples/template_method_pattern/theory.md
@@ -0,0 +1,384 @@
+# Template Method Pattern
+
+## Overview
+
+The Template Method Pattern is a behavioral design pattern that defines the skeleton of an algorithm in a base class, allowing subclasses to override specific steps of the algorithm without changing its overall structure. It uses inheritance to achieve code reuse and enforces a consistent algorithm structure across variations.
+
+## Intent
+
+- Define the skeleton of an algorithm in a base class
+- Let subclasses override specific steps without changing structure
+- Enforce consistent algorithm structure across variations
+- Eliminate code duplication through inheritance
+- Provide hooks for subclass customization
+- Implement the "Hollywood Principle" (Don't call us, we'll call you)
+
+## Problem
+
+Consider a data processing application that processes different file types:
+
+```python
+class CSVProcessor:
+ def process(self, filename):
+ data = self.read_csv_file(filename)
+ self.validate_csv_data(data)
+ result = self.transform_csv_data(data)
+ self.write_csv_file(result)
+
+class JSONProcessor:
+ def process(self, filename):
+ data = self.read_json_file(filename)
+ self.validate_json_data(data)
+ result = self.transform_json_data(data)
+ self.write_json_file(result)
+```
+
+Problems without Template Method:
+- Duplicated algorithm structure (read, validate, transform, write)
+- Difficult to enforce consistent processing steps
+- Hard to add new processing steps to all processors
+- Violates DRY (Don't Repeat Yourself) principle
+- No guarantee subclasses follow same process
+
+## Solution
+
+The Template Method Pattern defines the algorithm structure in base class:
+
+```python
+class DataProcessor(ABC):
+ def process(self, filename): # Template method
+ data = self.read_file(filename)
+ self.validate_data(data)
+ result = self.transform_data(data)
+ self.write_file(result)
+
+ @abstractmethod
+ def read_file(self, filename): pass
+
+ @abstractmethod
+ def validate_data(self, data): pass
+
+ # Subclasses only override specific steps
+```
+
+### Key Components
+
+1. **Abstract Class**: Defines template method and abstract operations
+2. **Template Method**: Defines algorithm skeleton (usually final/not overridable)
+3. **Abstract Operations**: Steps that must be implemented by subclasses
+4. **Hook Operations**: Optional steps with default implementation
+5. **Concrete Classes**: Implement abstract operations
+
+## Benefits
+
+- **Code Reuse**: Common algorithm structure defined once
+- **Consistency**: All subclasses follow same algorithm structure
+- **Open/Closed**: New variations added without modifying base class
+- **Inversion of Control**: Base class controls flow, subclasses provide details
+- **Reduces Duplication**: Common code in base class
+- **Enforces Process**: Template method ensures steps are executed in order
+
+## When to Use
+
+- When you have multiple classes with similar algorithms but different implementations
+- When you want to control the algorithm structure while allowing customization
+- When you want to avoid code duplication across similar classes
+- When you need to enforce a specific sequence of steps
+- When you want to provide hooks for subclass customization
+- When you have invariant parts of algorithm that should not change
+
+## Python-Specific Considerations
+
+Python allows template method with several approaches:
+
+- **ABC and abstractmethod**: Enforce implementation of required methods
+- **Hook Methods**: Provide default implementations that can be overridden
+- **Multiple Inheritance**: Combine template methods through mixins
+- **`super()`**: Chain template method variations
+- **NotImplementedError**: Alternative to abstractmethod
+
+### Using ABC
+
+```python
+from abc import ABC, abstractmethod
+
+class Template(ABC):
+ def template_method(self):
+ self.step1()
+ self.step2()
+
+ @abstractmethod
+ def step1(self): pass
+
+ def step2(self): # Hook with default
+ pass
+```
+
+## Implementation Approaches
+
+### 1. Classic Template Method
+Abstract base class with template method.
+
+### 2. Template Method with Hooks
+Provide optional hook methods with default behavior.
+
+### 3. Multi-Stage Template
+Multiple template methods for different phases.
+
+### 4. Parameterized Template
+Template method accepts parameters for customization.
+
+## Comparison with Other Patterns
+
+- **Strategy**: Encapsulates entire algorithm vs defines skeleton
+- **Factory Method**: Creates objects vs defines algorithm structure
+- **State**: Changes behavior based on state vs structure of algorithm
+- **Decorator**: Adds behavior vs defines algorithm skeleton
+
+## Example Use Cases
+
+- **Data Processing**: Read, validate, transform, write pipeline
+- **Web Scraping**: Fetch, parse, extract, store pipeline
+- **Game Development**: Initialize, update, render game loop
+- **Testing Frameworks**: Setup, execute, teardown test lifecycle
+- **Build Systems**: Configure, compile, link, package build process
+- **Report Generation**: Gather data, format, apply styles, output
+- **Authentication**: Validate credentials, check permissions, grant access
+- **HTTP Request Handling**: Parse request, authenticate, process, send response
+
+## Anti-Patterns to Avoid
+
+- **Too Many Steps**: Keep template method focused
+- **Too Deep Hierarchy**: Avoid deep inheritance chains
+- **Rigid Template**: Provide hooks for flexibility
+- **Leaking Abstractions**: Don't expose implementation details
+- **Override Template**: Template method should not be overridable
+- **Too Generic**: Keep template method specific to domain
+
+## Implementation Variations
+
+### 1. Simple Template
+Basic template method with abstract steps.
+
+### 2. Template with Hooks
+Include optional hook methods.
+
+### 3. Multi-Level Template
+Template methods at multiple inheritance levels.
+
+### 4. Template with Callbacks
+Use callbacks instead of inheritance.
+
+### 5. Template with Context
+Pass context object through steps.
+
+## Python Implementation Patterns
+
+### Using ABC
+
+```python
+from abc import ABC, abstractmethod
+
+class Template(ABC):
+ def template_method(self):
+ self.required_step()
+ self.optional_hook()
+
+ @abstractmethod
+ def required_step(self):
+ pass
+
+ def optional_hook(self):
+ # Default implementation
+ pass
+```
+
+### Using NotImplementedError
+
+```python
+class Template:
+ def template_method(self):
+ self.step1()
+ self.step2()
+
+ def step1(self):
+ raise NotImplementedError("Must implement step1")
+
+ def step2(self):
+ pass # Optional hook
+```
+
+### Final Template Method (Python 3.11+)
+
+```python
+from typing import final
+
+class Template:
+ @final
+ def template_method(self):
+ # Cannot be overridden
+ self.step1()
+ self.step2()
+```
+
+## Testing Considerations
+
+Template method pattern is testable:
+
+- **Test Template Flow**: Verify template method calls steps in order
+- **Test Subclasses**: Test each concrete implementation
+- **Mock Steps**: Mock individual steps to test template logic
+- **Test Hooks**: Verify hooks are called at right time
+- **Template Invariants**: Test that template structure is maintained
+
+## Best Practices
+
+- **Don't Override Template**: Template method should be final
+- **Minimize Required Steps**: Only make necessary steps abstract
+- **Provide Hooks**: Allow optional customization through hooks
+- **Clear Naming**: Use descriptive names for steps
+- **Document Process**: Document algorithm flow clearly
+- **Keep Steps Focused**: Each step should have single responsibility
+- **Use Final**: Mark template method as final (Python 3.11+)
+- **Composition Alternative**: Consider strategy pattern for more flexibility
+
+## Advanced Patterns
+
+### Template Method + Factory
+Factory creates objects with template methods.
+
+### Template Method + Strategy
+Template steps use strategies for variation.
+
+### Template Method + Hook
+Extensive use of hooks for customization.
+
+### Template Method + Observer
+Notify observers at each template step.
+
+## Real-World Examples
+
+### Testing Frameworks (pytest, unittest)
+`setUp()`, test execution, `tearDown()` template.
+
+### Django Class-Based Views
+`dispatch()`, `get()`, `post()` template for request handling.
+
+### Web Frameworks
+Request handling pipeline (parse, authenticate, process, respond).
+
+### Build Tools
+Build process template (configure, compile, link, package).
+
+## Performance Considerations
+
+- **Virtual Method Calls**: Slight overhead from dynamic dispatch
+- **Deep Hierarchies**: Avoid deep inheritance for performance
+- **Step Granularity**: Balance between fine-grained and coarse steps
+- **Caching**: Cache expensive step results when appropriate
+- **Lazy Evaluation**: Defer expensive steps until needed
+
+## Hook Methods
+
+Hooks provide extension points without requiring override:
+
+```python
+class Template:
+ def template_method(self):
+ self.before_processing() # Hook
+ self.process()
+ self.after_processing() # Hook
+
+ def before_processing(self):
+ pass # Optional hook
+
+ def after_processing(self):
+ pass # Optional hook
+
+ @abstractmethod
+ def process(self):
+ pass # Required step
+```
+
+## Hollywood Principle
+
+Template Method implements "Don't call us, we'll call you":
+
+- **Framework calls application**: Base class calls subclass methods
+- **Inversion of Control**: Parent class controls flow
+- **Subclass Plugs In**: Subclass provides implementations
+
+## When to Use Inheritance vs Composition
+
+**Use Template Method (Inheritance) When:**
+- Algorithm structure is stable and unlikely to change
+- Variations share substantial common code
+- Strong "is-a" relationship exists
+- You want to enforce process consistency
+
+**Use Strategy (Composition) When:**
+- Need runtime algorithm switching
+- Multiple orthogonal variations exist
+- Prefer composition over inheritance
+- Algorithm structure may change
+
+## Migration to Template Method Pattern
+
+When refactoring existing code:
+
+1. Identify similar algorithms in multiple classes
+2. Extract common algorithm structure
+3. Create abstract base class with template method
+4. Move common code to base class
+5. Identify varying steps
+6. Make varying steps abstract or hook methods
+7. Create concrete subclasses implementing steps
+8. Test that all variations work correctly
+9. Remove duplicated code from original classes
+
+## Template Method vs Strategy
+
+**Template Method:**
+- Uses inheritance
+- Algorithm structure in base class
+- Subclasses override steps
+- Static at runtime (set at object creation)
+- More rigid structure
+- Better for stable algorithm with variations
+
+**Strategy:**
+- Uses composition
+- Algorithm encapsulated in strategy objects
+- Can switch strategies at runtime
+- More flexible
+- Looser coupling
+- Better for interchangeable algorithms
+
+## Primitive Operations vs Hooks
+
+**Primitive Operations (Abstract):**
+- Must be implemented by subclasses
+- Core algorithm steps
+- Use `@abstractmethod`
+
+**Hooks (Concrete with default):**
+- Optional customization points
+- Have default implementation (often empty)
+- Subclasses can override if needed
+
+## Final Methods in Python
+
+Python 3.11+ supports `@final` decorator:
+
+```python
+from typing import final
+
+class Template:
+ @final
+ def template_method(self):
+ # Cannot be overridden in subclasses
+ self.step1()
+ self.step2()
+```
+
+Before Python 3.11, document that method should not be overridden.
diff --git a/tasks.md b/tasks.md
index c02c966..08072f1 100644
--- a/tasks.md
+++ b/tasks.md
@@ -5,7 +5,7 @@
- [x] Factory Method - Defines an interface for creating objects but lets subclasses decide which class to instantiate.
- [ ] Abstract Factory - Provides an interface for creating families of related objects without specifying their concrete classes.
- [x] Builder - Separates complex object construction from its representation, allowing step-by-step construction.
-- [ ] Prototype - Creates new objects by copying existing instances (cloning).
+- [x] Prototype - Creates new objects by copying existing instances (cloning).
## Behavioral Patterns
- [x] Observer - Defines a one-to-many dependency where when one object changes state, all dependents are notified.
@@ -14,9 +14,9 @@
- [ ] Iterator - Provides sequential access to elements without exposing underlying representation.
- [ ] Mediator - Defines an object that encapsulates how objects interact, promoting loose coupling.
- [ ] Memento - Captures and externalizes an object's internal state for later restoration without violating encapsulation.
-- [ ] State - Allows an object to alter its behavior when its internal state changes.
+- [x] State - Allows an object to alter its behavior when its internal state changes.
- [x] Strategy - Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
-- [ ] Template Method - Defines the skeleton of an algorithm in a base class, letting subclasses override specific steps.
+- [x] Template Method - Defines the skeleton of an algorithm in a base class, letting subclasses override specific steps.
- [ ] Visitor - Separates algorithms from the objects they operate on, allowing new operations without modifying classes.
## Communications Patterns (Structural)
@@ -24,6 +24,6 @@
- [ ] Bridge - Separates abstraction from implementation so they can vary independently.
- [x] Composite - Composes objects into tree structures to represent part-whole hierarchies, treating individual objects and compositions uniformly.
- [x] Decorator - Adds new functionality to objects dynamically without altering their structure.
-- [ ] Facade - Provides a simplified interface to a complex subsystem.
+- [x] Facade - Provides a simplified interface to a complex subsystem.
- [ ] Flyweight - Shares common state between multiple objects to save memory.
-- [ ] Proxy - Provides a surrogate or placeholder to control access to another object.
\ No newline at end of file
+- [x] Proxy - Provides a surrogate or placeholder to control access to another object.
\ No newline at end of file