Design Patterns
A collection of design patterns and idioms in python
Each pattern has its own tradeoffs and needs us to pay attention more to why we are choosing a certain pattern than how to implement it.


Creational Patterns: provide various object creation mechanism which increases flexibiity and reuse of existing code
Singleton:One instance, globally accessible. Think of it as the "only one" of something.
Factory:Creates objects without specifying the exact class. Imagine it as a "factory" that produces different types of items.
Abstract Factory:Creates families of related objects. Like a "factory" that produces products with different variations.
Builder:Constructs complex objects step-by-step. Think of it as building a house with different components.
Prototype:Creates new objects by copying an existing object. Like a "copy machine" for objects.
Structural Patterns:
Adapter:Converts the interface of a class into another interface clients expect. Think of it as an "adapter" that allows different plugs to fit into a socket.
Composite:Composes objects into tree structures to represent part-whole hierarchies. Think of it as a "folder structure" in a file system.
Decorator:Dynamically adds responsibilities to objects. Think of it as "decorating" an object with additional features.
Facade:Provides a simplified interface to a complex subsystem. Think of it as a "front desk" simplifying access to different departments in a company.
Flyweight:Shares objects to support large numbers of fine-grained objects. Think of it as using the same "template" multiple times.
Proxy:Provides a surrogate or placeholder for another object. Think of it as a "remote control" for another object.
Bridge:Separates abstraction from implementation. Think of it as decoupling the "remote control" from the specific device.
Behavioural Patterns:
Observer: Defines a one-to-many dependency between objects. Think of it as a "news subscription" where many subscribers are notified of new content.
Strategy: Defines a family of algorithms and makes them interchangeable. Think of it as choosing different "strategies" for solving a problem.
Command: Encapsulates a request as an object. Think of it as a "command" that can be executed and reversed.
Iterator: Provides a way to access the elements of an aggregate object sequentially. Think of it as a "iterator" for going through a list one item at a time.
State: Allows an object to alter its behavior when its internal state changes. Think of it as a "traffic light" changing its behavior based on its state (red, yellow, green).
Memento: Captures and externalizes an object's internal state. Think of it as creating "checkpoints" to restore an object to a previous state.
Template Method: Defines the skeleton of an algorithm in a method, deferring some steps to subclasses. Think of it as a "template" for cooking a dish with some steps that can be customized.
Chain of Responsibility: Avoids coupling the sender of a request to its receiver by giving multiple objects a chance to handle the request.
Interpreter: Defines a grammatical representation for a language and provides an interpreter to deal with this grammar.
Mediator: Defines an object that encapsulates how a set of objects interact.
Visitor: Represents an operation to be performed on the elements of an object structure.
Others
Apart from these we also have following patterns:
1. Scope
Class Patterns: These focus on relationships between classes and their subclasses. They tend to use inheritance to achieve their goals. Examples include patterns like Template Method and Factory Method.
Object Patterns: These deal with object relationships and are often more flexible than class patterns, as they rely on composition rather than inheritance. Examples include Singleton, Observer, and State.
2. Purpose
Instantiation Patterns: These specifically address the creation of instances. They often overlap with Creational patterns, such as Singleton and Factory.
Structural Composition Patterns: These patterns describe how classes and objects are structured to form larger structures. Examples are Composite and Decorator.
Behavioral Patterns: These involve how classes or objects communicate. This aligns with the classic Behavioral category but can also include specific sub-patterns like those for synchronization (e.g., Semaphore patterns).
3. Level of Granularity and Abstraction
Architectural Patterns: These are larger-scale patterns that typically encompass entire system designs, such as Model-View-Controller (MVC), Microservices, and Layered Architecture. They tend to work at a broader level than the classic design patterns.
Design-Level Patterns: These deal with smaller, specific parts of the system and often refer to individual components or interactions between them, such as Adapter or Proxy.
Idioms: These are language-specific patterns, like RAII in C++, which don’t apply universally but are best practices within a particular language or paradigm.
4. Usage in Modern Paradigms
Concurrency Patterns: These patterns help manage multi-threaded programming, focusing on the synchronization and safe sharing of resources (e.g., Active Object, Thread Pool).
Distributed Patterns: These are useful for designing systems that operate across multiple machines, such as Broker, Event-Driven Architecture, and Leader Election.
Reactive Patterns: Patterns like Publisher-Subscriber, Reactor, and Circuit Breaker are used in reactive programming, where the system is event-driven and responsive to changes.
5. Domain-Specific Patterns
In specific domains like UI design, patterns like Model-View-Controller (MVC), Model-View-Presenter (MVP), and Model-View-ViewModel (MVVM) are common.
In enterprise applications, patterns like Data Access Object (DAO), Repository, and Service Locator are useful for managing data and service interactions.
6. Evolution and Versioning of Patterns
Some patterns address how to manage evolving systems, like Versioned State, which is relevant for systems that need backward compatibility with older versions of software.
Importance of choosing the right design patterns
Scalability: Ensures the architecture can accommodate growth without excessive restructuring.
Flexibility: Enables easy adaptation to changing requirements and future enhancements.
Maintainability: Facilitates code readability and comprehension, easing maintenance tasks.
Reusability: Promotes reuse of proven solutions, saving development time and effort.
Performance: Optimizes code execution and resource utilization for efficient operation.
Reducing Errors: Helps avoid common pitfalls and design flaws, leading to fewer bugs and issues.
Creational Patterns:
When to use creation design pattern?
Creational design pattern is used when object creation is complex, involves multiple steps or requires specific initialization.
We must use them when the problem is related to object creation.
These are useful for promoting reusability, encapsulating creation logic and decoupling client code from classes it instantiates.
it enhances flexibility, making it easier to change or extend object creation methods at runtime.
Common patterns include singleton, Factory method, Abstract Factory, Builder and prototype.
We use them to improve maintainability, readability and scalability or the codebase,

Singleton
Ensures a class has only one instance and provides a global point of access to it.
Useful when a single instance of a class is needed to coordinate actions across the system (e.g., database connections, configuration settings).
Factory Method
Defines an interface for creating objects but allows subclasses to alter the type of objects created.
Commonly used when a class can't anticipate the type of object it needs to create, or when the class wants its subclasses to specify the objects it creates.
Abstract Factory
Provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Often used when the system needs to be independent of how its objects are created, composed, or represented, and when related objects need to work together.
Builder
Separates the construction of a complex object from its representation, allowing the same construction process to create different representations.
Useful when creating objects with multiple optional or required attributes, especially when the creation process is complex (e.g., constructing a car with customizable features).
Prototype
Creates new objects by copying an existing object, or "prototype."
Handy when creating objects is costly or complex, as it allows you to duplicate objects without going through the initialization process. It also enables dynamic and runtime object creation.
Object Pool
Manages a pool of reusable objects to improve performance, especially in scenarios where object creation is costly.
It’s often used in resource-constrained environments or with frequently reused objects like database connections or threads
Singleton pattern
ensures that a class has only one instance and provides a global access point to that instance.
This is useful when exactly one object is needed to coordinate actions across the system, such as in cases where a single point of control or shared resource (e.g., database connection, configuration settings) is required.
Key Characteristics of Singleton
Single Instance: Only one instance of the class is created and shared.
Global Access: The instance can be accessed globally via a static method.
Controlled Instantiation: The class itself controls the instantiation by restricting direct creation of objects.
Implementation Steps
Private Constructor: Make the constructor private to prevent other classes from creating new instances.
Static Instance Method: Provide a static method to get the single instance.
Lazy Initialization (optional): Delay the creation of the instance until it's requested (lazy loading).
Thread Safety (optional): Ensure thread safety if the Singleton will be used in a multi-threaded environment.
Singleton Code Example (Python)
Here’s a basic example of a thread-safe Singleton in Python using lazy initialization:
Explanation
Private Static Variable (
_instance): Stores the unique instance of the class.Lock for Thread Safety (
_lock): Ensures only one thread can initialize the instance at a time.Double-Checked Locking: The
__new__method checks if_instanceisNonetwice (once outside the lock and once inside), reducing unnecessary locking after the instance is created.Shared State: Both
singleton1andsingleton2are references to the same object, as verified bysingleton1 is singleton2.
Thread Safety in Singleton
The double-checked locking with threading.Lock ensures that the Singleton pattern remains thread-safe, meaning multiple threads won’t create separate instances of the Singleton.
Pros and Cons of the Singleton Pattern
Pros:
Controlled access to a single instance.
Reduces memory footprint as only one instance is created.
Useful for shared resources or services (e.g., logging, configuration).
Cons:
Can make unit testing more difficult due to the single shared instance.
Introduces global state, which can lead to tighter coupling and hinder modularity.
Can introduce unexpected dependencies if overused or misused.
The Singleton pattern is widely used, especially for managing shared resources, but should be applied thoughtfully to avoid excessive coupling in your design.
Factory Method
pattern defines an interface for creating objects but lets subclasses decide which class to instantiate.
Rather than calling a constructor directly, the client calls a factory method, which returns an instance of the appropriate subclass. This pattern is useful when:
A class cannot anticipate the type of objects it needs to create.
A class wants its subclasses to specify the objects it creates.
There is a need to manage or simplify object creation based on conditions.
Key Characteristics of Factory Method Pattern
Single Interface: The pattern defines a common interface for creating objects, but defers the instantiation to subclasses.
Encapsulation of Creation Logic: Creation of objects is encapsulated within factory methods, making it easier to modify or extend.
Promotion of Loose Coupling: The client code depends on an interface, not a concrete class, making it easy to change or add product types.
Implementation Structure
Creator (Abstract): Declares the factory method that returns new products. May define a default implementation or be fully abstract.
Concrete Creator: Implements the factory method to return instances of specific products.
Product (Abstract): Declares the interface for objects the factory method creates.
Concrete Product: Implements the product interface.
Factory Method Code Example (Python)
Let's create an example of a Transport Factory that can produce various types of transport (like cars and bikes) depending on the situation.
Step 1: Define the Product Interface
Step 2: Create Concrete Products
Step 3: Define the Creator Interface (Abstract Factory)
Step 4: Create Concrete Creators
Step 5: Using the Factory Method
Explanation
TransportInterface: Declares adelivermethod that all product types (Truck, Ship) must implement.Concrete Products (
TruckandShip): Implement theTransportinterface and provide specific delivery methods.LogisticsAbstract Class: The abstract creator class declares the factory methodcreate_transport, which subclasses must implement.Concrete Creators (
RoadLogisticsandSeaLogistics): Implement thecreate_transportmethod to return an instance ofTruckorShip, respectively.
Advantages of Factory Method
Loose Coupling: Clients use an interface to interact with products rather than concrete classes, making it easy to change or extend product types.
Single Responsibility: Each creator class is responsible only for creating products, simplifying object creation and testing.
Scalability: New product types can be added without altering existing code, adhering to the Open/Closed Principle.
Disadvantages of Factory Method
Increased Complexity: Introduces additional subclasses, which can complicate the design if overused.
May Overhead: For simple object creation, this pattern can add unnecessary complexity.
The Factory Method is ideal when the system needs to choose between multiple types of related objects without needing to modify the client code, thus enhancing maintainability and scalability.
Abstract Factory
provides an interface for creating families of related or dependent objects without specifying their concrete classes.
This pattern is particularly useful when a system must be independent of how its objects are created, composed, or represented, and when products need to work together in specific combinations.
Key Characteristics of Abstract Factory Pattern
Family of Products: It produces families of related products rather than a single product.
Consistency of Products: Ensures that products are compatible with each other within a family.
Encapsulation of Object Creation: Creation of products is encapsulated in "factories," making it easy to switch between different families by changing the factory.
Implementation Structure
Abstract Factory Interface: Declares the factory methods to create abstract products.
Concrete Factories: Implement the abstract factory interface, providing methods to create specific products.
Abstract Product Interfaces: Define interfaces for each product type.
Concrete Products: Implement the product interfaces and represent individual product types belonging to families.
Client: Uses the abstract factory and product interfaces, allowing for flexible product family changes.
Example Scenario
Let’s implement an example of an Abstract Factory to create two families of products: Victorian Furniture and Modern Furniture. Each family will contain two types of products: Chair and Sofa.
Code Example (Python)
Step 1: Define Abstract Product Interfaces
Step 2: Create Concrete Products for Each Family
Step 3: Define the Abstract Factory Interface
Step 4: Implement Concrete Factories for Each Family
Step 5: Client Code to Use Abstract Factory
Explanation
Product Interfaces (
ChairandSofa): Define abstract methods for chair and sofa products.Concrete Products: Each product family (Victorian and Modern) has its own specific implementations of
ChairandSofa.Abstract Factory Interface (
FurnitureFactory): Declares methods for creating each type of product (in this case,create_chairandcreate_sofa).Concrete Factories: Implement the
FurnitureFactoryto create specific products within the family, ensuring product compatibility.Client Code: Uses the factory interface to obtain and interact with products, without knowing the concrete factory type.
Output
Advantages of Abstract Factory
Consistency Among Products: Ensures that products created within a family are compatible, which is useful when objects need to work together seamlessly.
Encapsulation of Product Creation: Centralizes object creation, simplifying maintenance and making it easy to swap out or extend product families.
Scalability: New product families can be added without modifying existing client code, adhering to the Open/Closed Principle.
Disadvantages of Abstract Factory
Increased Complexity: Introduces additional interfaces and classes, which may lead to higher complexity for simpler problems.
Rigid Structure: Adding new product types may require changes to all existing factories, which could be restrictive if the types vary often.
The Abstract Factory pattern is ideal for systems requiring multiple, interchangeable families of related products, providing flexibility and a modular design for creating compatible objects.
Builder
separates the construction of a complex object from its representation. The Builder pattern allows you to create complex objects step-by-step, enabling different configurations of an object without changing its structure.
This pattern is particularly useful for creating objects with multiple optional parameters or configurations.
Key Characteristics of the Builder Pattern
Separation of Construction and Representation: The pattern isolates complex construction logic from the representation, allowing you to focus on constructing the object rather than understanding its details.
Incremental Construction: Step-by-step methods are used to configure the object, which is useful for creating various configurations.
Flexibility in Construction: Allows the same construction process to create different representations or configurations of an object.
Implementation Structure
Product: The complex object that is being built.
Builder Interface: Defines methods for creating parts of the Product.
Concrete Builder: Implements the Builder interface to construct and assemble the parts of the Product.
Director (optional): Directs the construction sequence using a Builder. This class is optional and is used when you want to standardize the construction process.
Example Scenario
Consider creating a House object, which can have different configurations like the number of rooms, the presence of a garage, a garden, and a swimming pool. Each configuration step will be handled by the builder, while the director will manage the order of construction.
Code Example (Python)
Step 1: Define the Product
Step 2: Create the Builder Interface
Step 3: Implement the Concrete Builder
Step 4: Create the Director (Optional)
Step 5: Using the Builder
Explanation
House: The product class that represents the object being constructed.
Builder Interface (
HouseBuilder): Declares the building steps that are required to construct a house.Concrete Builder (
ConcreteHouseBuilder): Implements the builder interface methods to configure various parts of the house.Director: Orchestrates the construction steps to create different configurations of the product. In this example, the
Directorcreates both a minimal and a luxury house by calling specific methods on the builder.Client Code: The client interacts with the
DirectorandBuilderto get different configurations of theHouseobject.
Advantages of Builder Pattern
Greater Control: Allows finer control over the construction process, useful for complex objects.
Flexible Configurations: Supports creating different configurations of an object without changing the client code.
Improved Readability and Maintenance: Separates the construction process from the representation, which simplifies code readability and maintenance.
Disadvantages of Builder Pattern
Increased Complexity: Introduces more classes and interfaces, which may not be necessary for simpler objects.
Potential Overhead: May add unnecessary overhead for objects with simple construction.
The Builder pattern is particularly useful when constructing objects that have several optional parts or configurations. It makes it easy to create complex products step-by-step while keeping the construction process separated from the product’s representation.
Prototype
allows cloning of objects, even complex ones, without depending on their concrete classes. It relies on a prototype (or template) object that can be copied to create new instances, making it useful when object creation is costly or complex. The Prototype pattern is particularly useful when:
You want to avoid creating an object from scratch.
Object creation is resource-intensive.
Different configurations of the object are needed frequently.
Key Characteristics of Prototype Pattern
Cloning Instead of Instantiation: Objects are created by copying (cloning) an existing object, not by using
newor constructors.Flexibility: Allows for creating complex objects dynamically at runtime with a flexible prototype interface.
Avoiding Subclassing: Supports object creation without subclassing, which can help reduce the overhead of additional classes.
Implementation Structure
Prototype Interface: Declares a
clonemethod for cloning itself.Concrete Prototype: Implements the
clonemethod to perform a deep or shallow copy.Client: Uses the
clonemethod to create new objects by copying the prototype.
Types of Cloning
Shallow Copy: Creates a new object, but nested objects are shared between the original and the clone.
Deep Copy: Creates a new object along with all nested objects, resulting in a fully independent clone.
Prototype Code Example (Python)
Let’s create an example where we have a Shape prototype with two concrete prototypes: Circle and Rectangle. The prototype objects can be cloned to create identical shapes.
Step 1: Define the Prototype Interface
Step 2: Create Concrete Prototypes
Step 3: Using the Prototype Pattern
Explanation
Prototype Interface (
Shape): Declares theclonemethod for creating copies of objects. This interface has acolorattribute shared by all shapes.Concrete Prototypes (
CircleandRectangle): Each shape class implements theclonemethod to return a deep copy of itself.Cloning Process: In
clone, we usecopy.deepcopyto create a completely independent copy of the object, including any nested data structures.Client Code: Clones the prototypes and modifies the properties of the clones without affecting the original prototypes.
Output
Advantages of Prototype Pattern
Reduces Resource Cost: Cloning an existing object is often cheaper than creating a new one, especially if the creation process is expensive.
Dynamic and Flexible: Makes it easy to add or change object configurations at runtime.
Decoupling from Concrete Classes: Avoids tight coupling to specific classes, promoting interface-based design.
Disadvantages of Prototype Pattern
Complexity with Deep Copying: Ensuring deep copies for complex structures can introduce additional complexity.
Hidden Dependencies: Can lead to unexpected dependencies if clones share references unintentionally in shallow copies.
Limited Control over Cloning Process: If the objects contain unique identifiers or references that shouldn’t be copied, the cloning process may require additional customization.
The Prototype pattern is ideal when object creation is expensive or involves complex initialization, allowing for efficient and flexible object creation through cloning. It provides an efficient way to create new instances by copying a prototype, with the flexibility to adjust the copies without impacting the original.
Object Pool
used to manage a pool of reusable objects. It’s particularly useful when object creation is expensive in terms of resources or time.
Instead of creating and destroying objects repeatedly, the Object Pool pattern reuses existing objects, improving performance in scenarios where a large number of objects with a similar life cycle are needed.
Key Characteristics of Object Pool Pattern
Object Reusability: Enables the reuse of objects that are expensive to create, such as database connections, network connections, or large data buffers.
Efficient Resource Management: Reduces overhead from frequent creation and deletion of resources.
Pooling: Manages a set of initialized and reusable objects, allowing clients to acquire and release objects as needed.
Typical Use Cases for Object Pool Pattern
Database Connection Pooling: Reuses established database connections rather than creating new ones for each request.
Thread Pooling: Reuses a limited number of threads to handle a large number of tasks.
Network Resource Pooling: Manages a limited number of network resources, such as HTTP connections, in web servers.
Implementation Structure
Pool Manager (Object Pool): Manages the available and in-use objects. Provides methods to acquire and release objects.
Reusable Object: Represents the object that will be reused, such as a database connection or buffer.
Code Example: Object Pool for Database Connections (Python)
In this example, we’ll create a simplified DatabaseConnection class that represents a connection to a database. The DatabaseConnectionPool will manage a pool of these connections, allowing clients to acquire and release connections as needed.
Step 1: Define the Reusable Object
Step 2: Create the Object Pool
Step 3: Using the Object Pool
Explanation
DatabaseConnection: Represents a simple reusable connection object. It has a
connectmethod to indicate it’s in use and adisconnectmethod to release it.DatabaseConnectionPool: Manages the pool of connections. It keeps a list of available connections and a set of in-use connections. When a client requests a connection, it removes one from the available pool, marks it as in use, and returns it. When the client releases a connection, it’s returned to the available pool.
Client Code: Demonstrates acquiring connections from the pool and releasing them. Attempting to acquire a connection when the pool is exhausted results in a message prompting the client to release a connection first.
Output
Advantages of Object Pool Pattern
Performance Improvement: Reduces the overhead of creating and destroying objects repeatedly, improving performance in resource-intensive scenarios.
Efficient Resource Utilization: Manages limited resources (e.g., database connections) efficiently by reusing them.
Improved Scalability: Enables scaling with a limited resource pool, essential in high-load systems.
Disadvantages of Object Pool Pattern
Complexity in Management: Requires careful management of resources, especially in systems with dynamic load or unpredictable demands.
Potential for Resource Leaks: Improperly released objects may lead to resource leaks if not managed correctly.
Limited Flexibility: May not be suitable for objects that require frequent reinitialization or have unique lifecycle requirements.
The Object Pool pattern is valuable in situations where creating and destroying resources repeatedly is costly. By reusing resources, the pattern reduces resource strain and improves system efficiency. It is particularly effective for managing a pool of limited resources in multi-threaded environments.
Builder Pattern
is a design pattern in object-oriented programming, part of the "Gang of Four" patterns, that helps construct complex objects step by step.
The main idea behind the Builder Pattern is to separate the construction of an object from its representation, allowing the same construction process to create different representations.
This pattern is especially useful when:
You need to create an object with many fields or optional parameters.
You want to make the object immutable.
You want to avoid having a long list of constructor parameters, often referred to as the "telescoping constructor anti-pattern."
Structure of the Builder Pattern
Builder: Defines methods for setting different parts of an object.
ConcreteBuilder: Implements the builder interface and constructs the object.
Director (Optional): Oversees the construction process but is optional.
Product: The final object that’s being constructed.
Example Code in Java
Let’s create an example of the Builder Pattern to build a Computer object.
Using the Builder Pattern
Here’s how to create a Computer object using the ComputerBuilder:
Explanation
Constructor Privacy: The constructor for
Computeris private and only accessible toComputerBuilder.Chaining: The builder provides methods that return the builder itself (
this), allowing for method chaining.Immutability: Once created,
Computerobjects are immutable.Code Readability: Creating an object with the builder pattern improves readability, as we don’t need to remember the order of parameters and only specify the ones we need.
Structural Patterns:
When to use structural design patterns
When we need to compose objects and classes into larger structures while keeping them flexible and efficient.
We must choose structural design patterns when the problem is related to object assembly.
These patterns are useful for clarifying relationships between classes, managing object hierarchies and altering interfaces without affecting clients.
it promotes code reuse, simplify system design and enhance scalability.
They are beneficial when dealing with complex systems, integration of new components or refactoring existing codebases.

Of course. Here is the revised table for structural Low-Level Design (LLD) patterns, with the purpose, use case, and example combined into a single "Basic Description" column for each pattern.
Adapter
Purpose: To allow objects with incompatible interfaces to collaborate. It acts as a bridge between two interfaces, making them compatible so they can work together seamlessly. Use Case: When you need to integrate a new class into an existing system that has an incompatible interface. This is common when working with legacy code or third-party libraries. Example: A power adapter that allows you to plug a device with one type of plug into an outlet with a different configuration. In software, an adapter could convert data from a third-party API's XML format to the JSON format used by your application.
Bridge
Purpose: To decouple an abstraction from its implementation so that the two can vary independently. This pattern is particularly useful when you need to avoid a permanent binding between an abstraction and its implementation. Use Case: When you need to extend a class in several independent dimensions. For instance, managing a graphical user interface (GUI) across multiple platforms where the UI controls (abstraction) need to be separated from their platform-specific renderings (implementation). Example: A household light switch is a good analogy; the switch (abstraction) can be a simple toggle or a dimmer, while the light fixture it controls (implementation) can be a ceiling light or a lamp. In software, this could be a media player with different user interfaces (abstractions) controlling various audio/video decoders (implementations).
Composite
Purpose: To compose objects into tree structures to represent part-whole hierarchies. This allows clients to treat individual objects and compositions of objects uniformly. Use Case: When your application's core model can be represented as a tree structure. This is useful in graphics applications for grouping shapes or in file systems for representing directories and files. Example: A file system where a directory can contain files and other directories. You can perform operations like 'get size' on both a single file and an entire directory, and the directory will recursively calculate the size of its contents.
Decorator
Purpose: To attach additional responsibilities or behaviors to an object dynamically. This provides a flexible alternative to subclassing for extending functionality. Use Case: When you need to add features to an object at runtime without altering its class. This is useful for avoiding a "class explosion" where you would otherwise need numerous subclasses to handle all possible combinations of features. Example: Ordering a coffee where you start with a base coffee and then "decorate" it with additions like milk, sugar, or whipped cream. Each addition modifies the final product's cost and description.
Facade
Purpose: To provide a simplified, high-level interface to a complex subsystem of classes. It hides the complexities of the system and provides a unified interface to the client. Use Case: When you need to make a complex system easier to use by providing a simple interface to a set of interfaces in that subsystem. This is useful for interacting with sophisticated libraries or frameworks where you only need a subset of the functionality. Example: A customer placing a food order at a restaurant interacts with a waiter (the facade), who then handles all the complex interactions with the kitchen, billing, etc., shielding the customer from those details.
Flyweight
Purpose: To minimize memory usage or computational expenses by sharing as much as possible with other similar objects. It's used when you have a large number of objects that share common state. Use Case: When an application creates a large number of similar objects and memory consumption is a concern. The pattern is effective when much of the object's state can be made extrinsic (context-dependent) and shared. Example: In a text editor, instead of creating a separate object for every character, the flyweight pattern allows for sharing character objects. For example, all instances of the letter 'A' with the same font and size can share a single object.
Proxy
Purpose: To provide a surrogate or placeholder for another object to control access to it. This allows you to perform actions either before or after the request gets to the original object. Use Case: When you need to control and manage access to objects, such as for lazy loading, security control, logging, or caching. Example: A credit card acts as a proxy for your bank account. It provides a way to access your funds without carrying around cash and adds a layer of security. In software, a proxy can be used to load large images only when they are about to be displayed.
Adapter Pattern
allows objects with incompatible interfaces to work together by providing a "middle layer" or adapter.
This pattern involves a class or object that wraps one interface to make it compatible with another, allowing systems to interact seamlessly without modifying the existing code.
The Adapter Pattern can be implemented in two main ways:
Class Adapter: Uses inheritance to adapt one interface to another.
Object Adapter: Uses composition to adapt an interface by containing an instance of the incompatible class.
When to Use the Adapter Pattern
When you need to use a legacy class with a different or incompatible interface in a new system.
When you want to make a class reusable in multiple situations by adapting its interface.
When classes have similar functionality but different interfaces, and you want them to work together.
Example Scenario
Imagine you’re building an audio player that supports only MP3 files, but now you want to add support for other formats like MP4 or VLC. Instead of modifying the existing player class, you can create adapters for each new format, allowing them to work with the existing player without changing its code.
Example Code for Adapter Pattern (Python)
We’ll create an example with:
MediaPlayer: An existing interface that supports only MP3 files.
AdvancedMediaPlayer: A class that can play MP4 and VLC files.
MediaAdapter: An adapter that enables
MediaPlayerto play both MP4 and VLC files.
Step 1: Define the MediaPlayer Interface and Its Implementation
Step 2: Define the AdvancedMediaPlayer Interface and Implementations
Step 3: Create the MediaAdapter to Bridge MediaPlayer and AdvancedMediaPlayer
The MediaAdapter will implement MediaPlayer while also containing an instance of AdvancedMediaPlayer, allowing it to translate calls from MediaPlayer to AdvancedMediaPlayer.
Step 4: Use the Adapter in a Media Player
Explanation of the Adapter Pattern Implementation
MediaPlayer (Target Interface): This is the target interface that the client code expects to use, designed initially for playing MP3 files.
AdvancedMediaPlayer (Adaptee): This class has the existing incompatible methods for playing MP4 and VLC files.
MediaAdapter (Adapter): The adapter class implements
MediaPlayerbut usesAdvancedMediaPlayerinternally. When asked to play an MP4 or VLC file, it uses the appropriate method fromAdvancedMediaPlayer.AudioPlayer (Client): This client uses
MediaAdapterto support additional media types without altering its structure.
Output
Advantages of the Adapter Pattern
Single Responsibility Principle: You can separate the interface or logic of two incompatible classes.
Increased Reusability: Allows the reuse of existing classes even if they have incompatible interfaces.
Flexibility: You can add adapters for additional incompatible classes without changing the original code.
Disadvantages of the Adapter Pattern
Increased Complexity: Adding extra classes to handle adaptation can increase code complexity, especially with multiple adapters.
Overuse: Using the adapter pattern for classes that could be refactored or merged can lead to redundant code.
Class Adapter vs. Object Adapter
Class Adapter: Uses inheritance. This approach may be simpler but is limited to languages that support multiple inheritance.
Object Adapter: Uses composition (as shown in the example above). This approach is more flexible and works with languages that do not support multiple inheritance.
Summary
The Adapter Pattern is ideal when:
You need to make an existing class compatible with another interface.
You want to reuse a class that doesn’t match the current system’s interface.
By creating an adapter, you enable smooth integration without changing the code of either the client or the incompatible class, keeping your code more modular and flexible.
Bridge Pattern
decouples an abstraction from its implementation, allowing them to vary independently.
This pattern is useful when a class has multiple dimensions of variation, and it's important to keep these variations separate.
Key Concepts of the Bridge Pattern
Abstraction: The interface for the higher-level control logic. It typically maintains a reference to the implementation part.
Refined Abstraction: A subclass of the Abstraction that extends its behavior.
Implementor: An interface for the lower-level implementation.
Concrete Implementor: Implements the Implementor interface.
The goal of the Bridge Pattern is to separate the abstraction (high-level control) from the implementation (low-level control) so they can be developed and maintained independently.
Use Case Example
Consider a scenario where we want to create different devices (like TVs and Radios) with two different types of remotes (BasicRemote and AdvancedRemote). Using the Bridge Pattern allows us to separate the device (abstraction) from the remote controls (implementation).
Example Code in Java
Using the Bridge Pattern
Explanation
Decoupling Abstraction and Implementation: The
Deviceinterface represents the abstraction that can vary independently (TV, Radio), and theRemoteControlinterface represents the remote’s functionality.Enhanced Flexibility: By using composition, we can mix and match
Deviceimplementations with differentRemoteControlimplementations.Scalability: It’s easy to add new device types or new remote control types without affecting existing code, making this a scalable and flexible solution.
This pattern enables you to change the class structure more easily, as the bridge allows the two parts to evolve separately.
Composite Pattern
allows you to compose objects into tree-like structures to represent part-whole hierarchies.
This pattern treats individual objects and compositions of objects uniformly, making it easier to work with complex structures of objects as if they were single objects.
Key Concepts of the Composite Pattern
Component: Defines the interface for all objects in the composition (both leaf and composite nodes).
Leaf: Represents the basic building block objects that don’t have any children.
Composite: A component that has children and can hold other components. It implements methods to add, remove, or get children.
The Composite Pattern is particularly useful for building hierarchical structures like a tree of folders and files, organizational hierarchies, or graphical shapes.
Example Use Case: Files and Folders
In this example, we'll represent a file system where folders can contain both files and other folders. Each file and folder can be treated uniformly.
Example Code in Java
Using the Composite Pattern
Here’s how to create a folder structure using the Composite Pattern:
Output
Explanation
Component Interface:
FileSystemComponentis the common interface for bothFileandFolder. This allows treating files and folders in the same way.Leaf:
Fileis the leaf node. It has no children, and it implements theshowDetails()method to display its own details.Composite:
Folderis the composite node. It contains a list ofFileSystemComponentobjects, allowing it to contain both files and folders.Hierarchy Management: The
Folderclass provides methods to add and remove child components. This allows building a recursive structure of folders and files.Uniform Treatment: The client code treats
FileandFolderuniformly through theFileSystemComponentinterface, displaying the details of files and folders recursively.
Benefits of the Composite Pattern
Simplifies client code by allowing it to treat individual objects and compositions uniformly.
Supports complex hierarchies by enabling the creation of nested structures.
Extensibility: New component types (e.g., new types of files or folders) can be added without altering existing code, as long as they implement the
FileSystemComponentinterface.
The Composite Pattern is ideal for cases where part-whole hierarchies need to be managed and treated as single units.
Decorator Pattern
allows you to dynamically add new behaviors or functionalities to objects without altering their structure.
This is achieved by wrapping the object with a series of decorator classes, each adding its own behavior.
The pattern is useful when you want to extend the functionality of classes in a flexible and reusable way.
Key Concepts of the Decorator Pattern
Component: The common interface for both the original object and its decorators. This interface defines methods that can be enhanced by the decorators.
Concrete Component: The original class whose functionality needs to be extended.
Decorator: An abstract class implementing the
Componentinterface, containing a reference to aComponentobject. Decorators delegate tasks to the component and can add extra behaviors.Concrete Decorators: These classes extend the
Decoratorclass and add specific functionalities to theComponent.
The Decorator Pattern is commonly used in I/O streams in Java (e.g., BufferedInputStream decorating a FileInputStream) and in graphical user interfaces where visual elements may be decorated with borders, colors, or shadows.
Example Use Case: Coffee Shop
Let’s say we have a coffee shop, and we want to allow customers to add various ingredients to their coffee (like milk, sugar, and mocha). Using the Decorator Pattern, we can add these ingredients without changing the Coffee class itself.
Example Code in Java
Using the Decorator Pattern
Here's how to use the decorators to create different coffee combinations:
Output
Explanation
Component Interface:
Coffeedefines the methodsgetDescriptionandgetCostthat each coffee object and decorator must implement.Concrete Component:
SimpleCoffeeis the base type of coffee, and its behavior can be enhanced by decorators.Base Decorator:
CoffeeDecoratorholds a reference to aCoffeeobject and implements theCoffeeinterface, forwarding calls to the decorated coffee object.Concrete Decorators:
MilkDecorator,SugarDecorator, andMochaDecoratoradd their own functionalities by extendingCoffeeDecorator. Each decorator modifies the description and cost based on the ingredient added.Flexible Combinations: The coffee object can be wrapped in any number of decorators, each adding a specific ingredient, allowing for flexible combinations.
Benefits of the Decorator Pattern
Flexibility in extending functionality: The pattern allows adding features to an object without modifying its structure.
Adheres to the Open/Closed Principle: New functionality can be added by creating new decorators, without changing existing code.
Combines behaviors dynamically: You can add or remove decorators at runtime, giving dynamic control over the object’s behavior.
The Decorator Pattern is ideal for cases where multiple features or behaviors need to be added to an object in various combinations, without using inheritance.
Facade Pattern
provides a simplified interface to a complex subsystem. It hides the complexity of multiple classes and interactions by providing a single interface that clients can use to perform complex operations.
This pattern is often used to make a library, framework, or set of classes easier to use.
Key Concepts of the Facade Pattern
Facade: The main class that simplifies the interaction with the complex subsystem. It provides a single interface through which the client interacts with the system.
Subsystem Classes: These are the classes that perform the actual work but are hidden behind the facade. Each class in the subsystem has its own responsibilities and functions independently of the facade.
The Facade Pattern is useful when you want to provide a simple interface to a large, complex subsystem that the client does not need to understand in detail.
Example Use Case: Home Theater System
Imagine you have a home theater system with various components like an amplifier, projector, lights, and a DVD player. Turning on the home theater for a movie night requires multiple steps across each component. With the Facade Pattern, we can provide a single HomeTheaterFacade class to handle the steps automatically.
Example Code in Java
Using the Facade Pattern
Output
Explanation
Subsystem Classes:
Amplifier,Projector,Lights, andDVDPlayerare individual components in the home theater system. Each class provides its own specific functionality.Facade:
HomeTheaterFacadeprovides a simplified interface to control all the components of the home theater system. It encapsulates the complex interactions between components in methods likewatchMovieandendMovie.Single Entry Point: The client only interacts with
HomeTheaterFacade, simplifying the process of setting up and shutting down the home theater.
Benefits of the Facade Pattern
Simplifies Interface: Reduces the complexity of the system by hiding complex interactions behind a single, simplified interface.
Promotes Loose Coupling: The facade decouples the client from the subsystem, allowing the subsystem to evolve without impacting the client code.
Improves Code Readability: Facade code often represents a high-level overview of the system’s main tasks, making the code more readable and organized.
When to Use the Facade Pattern
When you have a complex subsystem that needs to be simplified for easier use.
When you want to decouple a system from its clients to reduce dependencies.
When you have several tightly coupled classes in a subsystem that would benefit from a single entry point for interaction.
The Facade Pattern is ideal when you need to simplify complex systems and create a single entry point that can manage multiple interactions behind the scenes.
Flyweight Pattern
allows you to reuse objects efficiently by sharing them across multiple contexts, instead of creating new instances for each context.
This is especially useful when there are a large number of similar objects, and creating a new object for each use would be costly in terms of memory.
The pattern minimizes memory usage by storing shared parts of objects in a central place and referencing them in different contexts. It distinguishes between intrinsic (shared) and extrinsic (unique per instance) states:
Intrinsic State: The shared state that does not change across instances and can be stored in the flyweight object.
Extrinsic State: The context-specific state that varies for each instance and is passed to the flyweight object when used.
The Flyweight Pattern is often used in applications that involve large numbers of similar objects, like rendering text characters or graphics.
Key Concepts of the Flyweight Pattern
Flyweight Interface: Defines the interface for flyweight objects. It provides methods to receive the extrinsic state (data unique to each instance).
Concrete Flyweight: Implements the flyweight interface and stores the intrinsic state. It uses extrinsic data provided by the client to perform operations.
Flyweight Factory: Manages and shares flyweight instances. It ensures that identical flyweights are shared instead of creating new ones.
Client: The client is responsible for storing or calculating extrinsic data and passing it to the flyweight.
Example Use Case: Text Character Rendering
Let’s say we are rendering a document containing millions of characters. Each character can be rendered with different font sizes, colors, and other properties. Rather than creating an object for each character, we can use the Flyweight Pattern to store a shared object for each unique character type, reducing memory usage.
Example Code in Java
Output
Explanation
Flyweight Interface: The
Characterinterface defines thedisplaymethod, which accepts the font size (extrinsic state) as a parameter.Concrete Flyweight:
ConcreteCharacterimplements theCharacterinterface and stores the intrinsic state (the symbol itself).Flyweight Factory:
CharacterFactoryis responsible for managing and reusingConcreteCharacterinstances. It stores previously created instances in aMapand returns an existing instance if it already exists.Client Code: The
Mainclass uses theCharacterFactoryto create and display characters. The font size is passed as an extrinsic state, allowing the same character object to be reused with different font sizes.
Benefits of the Flyweight Pattern
Memory Efficiency: The pattern reduces the number of objects created, leading to lower memory usage.
Performance Boost: Fewer objects mean fewer memory allocations, which can improve performance.
Reuse of Objects: Allows reusing objects with shared properties, which helps manage resources effectively.
When to Use the Flyweight Pattern
When your application creates a large number of objects, and this causes memory or performance issues.
When most object states can be made extrinsic and passed in from the client, making objects sharable.
When the application requires a large number of similar objects that can benefit from a centralized management approach.
The Flyweight Pattern is ideal when working with repetitive, memory-intensive data, such as rendering characters, graphics, or large datasets with limited variations.
Proxy Pattern
provides a placeholder or surrogate object to control access to another object.
It is often used when accessing an object directly could be undesirable, such as when the object is resource-intensive to create, is located remotely, or requires additional control before access.
The Proxy Pattern allows you to add a layer of control, making it useful for scenarios like lazy initialization, access control, logging, caching, and monitoring.
Types of Proxies
Virtual Proxy: Controls access to a resource that may be expensive to create. It creates the object only when needed (e.g., lazy initialization).
Protection Proxy: Manages access to an object based on user permissions or access levels.
Remote Proxy: Represents an object that resides in a different address space, such as a remote server.
Smart Proxy: Adds additional actions when an object is accessed, such as reference counting, logging, or caching.
Example Use Case: Image Loading with Virtual Proxy
Imagine a situation where you have an application that displays images. Loading all images at once can be resource-intensive. Using a virtual proxy, we can create a placeholder for each image, loading it only when it is accessed for the first time.
Example Code in Java
Output
Explanation
Subject Interface:
Imagedefines thedisplaymethod, which both the real image and proxy implement.Real Subject:
RealImageis the actual image class that loads an image from the disk and displays it.Proxy:
ImageProxyis a virtual proxy that holds a reference toRealImage. Thedisplaymethod in the proxy checks if theRealImageinstance is null and creates it only if necessary. This lazy initialization avoids loading the image until it’s actually needed.Client: The client uses
ImageProxyinstances forimage1andimage2. The first call todisplayon each proxy triggers the loading of the actual image, but subsequent calls just display it without reloading.
Benefits of the Proxy Pattern
Control Access: Can manage access to the real object based on permissions, conditions, or other constraints.
Lazy Initialization: Useful for delaying the instantiation of resource-intensive objects until they are needed.
Additional Functionality: Proxies can add functionality such as logging, caching, and counting references.
Remote Access: Enables remote access to objects as if they were local.
Common Uses of the Proxy Pattern
Virtual Proxy: For delaying expensive resource creation or loading, such as images, files, or connections.
Protection Proxy: For access control based on user roles, commonly used in systems with sensitive data.
Remote Proxy: In distributed systems where objects are located in remote servers (e.g., RMI in Java).
Smart Proxy: For logging, caching, and monitoring purposes.
When to Use the Proxy Pattern
When accessing an object directly is expensive and can be delayed.
When additional actions are required before or after accessing an object.
When you want to control access based on certain conditions, such as permissions or availability.
When working with remote or distributed objects that need to appear as local.
The Proxy Pattern provides flexibility and control over object access, making it a useful design pattern for many situations requiring indirect control or deferred initialization.
Behavioral Patterns:
We choose behavioral design pattern when we need to manage algorithms, communication or responsibilities between objects.
They are useful for encapsulating behavior that varies and promoting loose coupling between objects.
We choose behavior design pattern when the problem is related to object interations.
These facilitate code reuse, flexibility and maintainability by defining how objects interact and communicate.
Use them to address scenarios like handling complex workflows, managing state transitions or implementing communication between objects.

Chain of Responsibility
Purpose: Allows passing a request along a chain of handlers. Each handler decides whether to process the request or pass it to the next handler.
Use Case: Logging systems, user request processing pipelines.
Example: Middleware chain in web applications where each middleware can decide to process or pass the request.
Command
Purpose: Encapsulates a request as an object, allowing parameterization of requests and supporting undoable operations.
Use Case: Implementing undo/redo functionality, queuing tasks, and logging changes.
Example: Remote control operations where each button press is encapsulated as a command.
Interpreter
Purpose: Defines a language's grammar and uses an interpreter to interpret sentences in the language.
Use Case: Situations that involve language processing, such as SQL or mathematical expressions.
Example: Parsing and evaluating expressions in a calculator or SQL-like language interpreter.
Iterator
Purpose: Provides a way to access elements of an aggregate object sequentially without exposing its underlying structure.
Use Case: Iterating over complex data structures without exposing their internal representation.
Example:
Iteratorclasses in collections (e.g.,ArrayList.iterator()in Java).
Mediator
Purpose: Defines an object that encapsulates how a set of objects interact, promoting loose coupling between components.
Use Case: Reducing dependencies in systems with many interacting objects.
Example: Chatroom mediator where users communicate through a central mediator.
Memento
Purpose: Captures an object’s state to restore it later without exposing its details.
Use Case: Undo or rollback functionality.
Example: Text editor with "undo" functionality using snapshots of text states.
Observer
Purpose: Establishes a one-to-many dependency where when one object changes, its dependents are notified and updated automatically.
Use Case: Event handling systems, real-time data feeds.
Example: GUI components where changes in the model reflect on the view (e.g., MVC pattern).
State
Purpose: Allows an object to alter its behavior when its internal state changes, appearing as though it changed its class.
Use Case: Systems where objects' behavior depends on their state.
Example: Traffic lights that change behavior based on light color.
Strategy
Purpose: Defines a family of algorithms, encapsulates each one, and makes them interchangeable.
Use Case: When you need to switch algorithms or behaviors at runtime.
Example: Sorting strategies like quicksort, mergesort, or bubble sort in a context where the algorithm can vary.
Template Method
Purpose: Defines the skeleton of an algorithm, letting subclasses redefine certain steps without changing the algorithm’s structure.
Use Case: When a method’s structure is fixed but certain steps vary.
Example: Abstract classes in frameworks where the framework provides the skeleton, and the user provides specific implementations.
Visitor
Purpose: Separates an algorithm from the objects on which it operates, allowing new operations to be added without modifying object classes.
Use Case: When you need to perform operations across a complex object structure without changing classes.
Example: File system traversal where you want to perform different actions on files and directories
Chain of Responsibility
allows a request to pass through a series of handlers, where each handler can either process the request or pass it to the next handler in the chain.
This pattern promotes loose coupling, as the sender of a request doesn’t need to know which component will handle it, or how many handlers there are.
Key Concepts of the Chain of Responsibility Pattern
Handler Interface: Defines an interface for handling requests. It typically has a method to set the next handler in the chain and a method to handle the request.
Concrete Handlers: Implement the handler interface and either process the request or pass it to the next handler.
Client: Creates a chain of handlers and submits a request.
This pattern is particularly useful for situations where multiple handlers can handle the same type of request, and the request should be passed along the chain until it’s handled.
Example Use Case: Request Logging and Processing
Imagine a system where each request goes through several stages, such as logging, authorization, and validation, before reaching the main processing stage. Each of these stages can be handled by a separate handler in a chain, and if one handler cannot handle the request, it passes it to the next handler.
Example Code in Java
Output
Explanation
Handler Interface:
RequestHandleris an abstract class that defines ahandleRequestmethod and asetNextHandlermethod to chain handlers together.Concrete Handlers:
LoggingHandlerlogs the request and passes it along the chain.AuthorizationHandlerchecks if the request is authorized. If it is, the request is passed along; otherwise, it stops.ValidationHandlervalidates the request format. If valid, it passes it along; if invalid, it stops.
Client: The
Mainclass creates each handler, sets up the chain, and submits requests. Each request passes from handler to handler until it’s either processed or rejected.
Benefits of the Chain of Responsibility Pattern
Decoupling of Sender and Receiver: The client doesn’t need to know which handler will process the request.
Flexible Chains: Handlers can be added, removed, or reordered without modifying the client code.
Single Responsibility: Each handler has one specific responsibility, promoting cleaner, more maintainable code.
Drawbacks
Performance Overhead: Passing requests along a long chain can impact performance.
Difficult Debugging: When many handlers are in the chain, it may become difficult to trace the request’s flow.
Common Use Cases
Event Handling Systems: In GUI applications, different components handle events such as mouse clicks, with each component deciding to process the event or pass it on.
Customer Support System: Requests are escalated through different support levels until they’re resolved.
Logging Frameworks: Different loggers handle messages based on priority, with lower priority messages passed up the chain if they’re not handled.
The Chain of Responsibility Pattern is useful when multiple handlers might handle requests in specific scenarios or in a specific order, allowing flexibility and reusability.
Command Pattern
turns a request into a standalone object. This object contains all the information about the request, such as the operation to be performed, the receiver of the request, and any parameters required.
By doing so, the pattern decouples the sender of the request from the receiver, allowing for operations to be parameterized, queued, logged, and even undone or redone.
Key Concepts of the Command Pattern
Command Interface: Declares an abstract method for executing commands.
Concrete Command: Implements the command interface and defines a binding between an action and a receiver.
Receiver: The actual component that performs the action or business logic.
Invoker: Initiates the command. It knows how to execute a command but doesn’t know the details of what the command does.
Client: Creates concrete commands and sets the receivers.
Example Use Case: Remote Control for Home Appliances
Imagine you have a remote control that can turn on and off different devices, like a light or a fan. Instead of having a direct dependency on each device, we can create command objects for each action, making the remote flexible and extensible.
Example Code in Java
Output
Explanation
Command Interface:
Commanddeclares theexecuteandundomethods. These methods encapsulate the behavior that will be triggered by theInvoker.Concrete Commands:
LightOnCommandencapsulates the action of turning the light on, with theexecutemethod invokinglight.turnOn()andundoturning the light off.LightOffCommandencapsulates turning the light off, withexecutecallinglight.turnOff()andundoturning the light on.
Receiver:
Lightclass has methods to turn the light on and off.Invoker:
RemoteControlholds a reference to a command and triggers itsexecuteandundomethods, depending on which button is pressed.Client: The
Mainclass createsLightOnCommandandLightOffCommandobjects, sets them on theRemoteControl, and executes them.
This setup allows for commands to be added, removed, or changed without altering the RemoteControl or Light classes, adhering to the Open/Closed Principle.
Benefits of the Command Pattern
Decoupling: Separates the object that invokes the command from the one that knows how to execute it.
Extensibility: New commands can be added without modifying existing code.
Undo/Redo Support: Supports operations to be undone or redone by storing state in the command.
Queuing and Logging: Commands can be queued for delayed execution or logged for audit purposes.
Drawbacks
Complexity: Adding commands for every operation can lead to more classes, which might add complexity.
Memory Overhead: If using undo functionality, commands may need to store the state, which could increase memory usage.
Common Use Cases
Undo/Redo Operations: Text editors, drawing applications, and IDEs.
Action Queueing: Scheduling operations for later execution.
Macro Recording: Grouping commands into a sequence that can be executed as a unit (like a macro).
Remote Control Applications: Any control mechanism that needs to trigger commands on different devices (e.g., smart home systems).
The Command Pattern is a powerful way to encapsulate requests and add flexibility in controlling actions, making it highly suitable for applications needing dynamic and extensible command processing.
Interpreter Pattern
used to evaluate and interpret sentences or expressions in a given language.
This pattern is useful for designing simple language interpreters or implementing rules and grammars, making it ideal for parsing expressions in a specific problem domain, such as mathematical expressions, log filters, or parsing configuration files.
Key Concepts of the Interpreter Pattern
Context: Provides the environment or context in which expressions are interpreted. It holds data values, variable assignments, and other relevant information needed for evaluation.
Abstract Expression: Defines an interface for interpreting expressions. Each specific expression type will implement this interface.
Terminal Expression: Represents specific objects in the language (e.g., numbers, constants) and evaluates directly. These are the "leaf" nodes in the pattern structure.
Non-Terminal Expression: Combines terminal and other expressions. For example, addition or subtraction expressions in an arithmetic interpreter.
Example: Arithmetic Expression Interpreter
In this example, we'll build a simple interpreter for arithmetic expressions involving addition and subtraction.
Step-by-Step Code
Define the Expression Interface: This interface will be implemented by all expressions in the language.
Implement Terminal Expressions: The
Numberclass here represents a number in the arithmetic expression.Implement Non-Terminal Expressions: We’ll create two non-terminal expressions:
AddandSubtract.Create a Context and Client Code: We can now use these classes to interpret a basic arithmetic expression, such as
(5 + 10) - 3.
Explanation of the Code
Number: This is a terminal expression that simply returns the number itself.
Add and Subtract: These are non-terminal expressions that interpret expressions by evaluating the left and right sub-expressions.
expression: We construct an arithmetic expression
(5 + 10) - 3by composingAddandSubtractobjects, demonstrating how the interpreter pattern can build complex expressions by combining smaller expressions.
When to Use the Interpreter Pattern
When you have a language to interpret, and you can represent statements in this language as an abstract syntax tree (AST).
When the grammar of the language is relatively simple and doesn’t need heavy optimization.
In applications that require configurable rule-based logic, like converting user commands, translating protocols, or evaluating mathematical expressions.
Pros and Cons
Pros:
Simple, easy-to-extend implementation for small languages.
Flexible, as new expressions can be added without changing the existing codebase.
Cons:
Can become complex and inefficient if the language or grammar is large.
Inefficient for performance-sensitive applications due to deep recursive calls.
Iterator Pattern
provides a way to access the elements of a collection (such as an array, list, or tree) sequentially without exposing its underlying representation.
This pattern is particularly useful for traversing complex data structures in a standardized way, promoting encapsulation and separation of concerns.
Key Concepts of the Iterator Pattern
Iterator Interface: Defines methods to traverse elements in a collection (such as
next,hasNext, etc.).Concrete Iterator: Implements the iterator interface for a specific collection, holding a reference to the collection and a cursor (or index) to keep track of the current position.
Aggregate (Collection): The collection or aggregate object that provides an
iterator()method to return an iterator object.Client: Uses the iterator to access elements in the collection without needing to know the details of the underlying data structure.
Example: Custom List Iterator
In this example, we will build a custom list iterator for a NameCollection class.
Step-by-Step Code
Define the Iterator Interface: This interface defines the methods for traversal.
Implement the Collection (Aggregate) Interface: This interface provides a method to get an iterator.
Implement the Concrete Collection: We’ll create a
NameCollectionclass that stores names and returns an iterator.Implement the Concrete Iterator: The
NameIteratorclass will implement theIteratorinterface to traverse theNameCollection.Client Code to Use the Iterator: Now we can use the
NameIteratorto iterate throughNameCollection.Output:
Explanation of the Code
NameCollection: This class represents the collection and provides a
get_iteratormethod to return an iterator for its elements.NameIterator: Implements
Iteratorto allow sequential access to the elements inNameCollection. It maintains an index to keep track of the current position.Client Code: Uses the
NameIteratorto traverse theNameCollectionwithout directly accessing its internal structure.
When to Use the Iterator Pattern
When you need to traverse a complex data structure without exposing its details.
When you have multiple ways of traversing a collection, and each traversal can be implemented as a separate iterator.
When you want to provide a uniform traversal interface across different types of collections (e.g., lists, trees, graphs).
Pros and Cons
Pros:
Promotes encapsulation by hiding the collection’s internal structure.
Provides a uniform way to traverse different types of collections.
Supports multiple traversal strategies by creating different iterator implementations.
Cons:
May add extra complexity when used with simple collections (such as lists).
Increases the memory footprint, as iterators need to maintain state for tracking traversal.
Mediator Pattern
facilitates communication between objects (known as "colleagues") by centralizing complex communication logic in a separate mediator object.
This pattern promotes loose coupling, as the colleagues don't need to directly reference or interact with each other; instead, they communicate through the mediator.
Key Concepts of the Mediator Pattern
Mediator Interface: Defines the interface for communication between colleagues.
Concrete Mediator: Implements the mediator interface and coordinates communication between colleague objects, managing dependencies and interactions.
Colleague Classes: Represent components or classes that communicate with each other through the mediator. Each colleague holds a reference to the mediator and uses it to send messages to other colleagues.
Client: Instantiates the colleagues and the mediator, and triggers communication through the mediator.
Example: Chat Room Mediator
In this example, we’ll create a simple chat room where different users (colleagues) can send messages to each other through a mediator.
Step-by-Step Code
Define the Mediator Interface: This interface provides a method for colleagues to send messages.
Implement the Concrete Mediator: The
ChatRoomclass implements the mediator interface to manage communication between users.Define the Colleague Class: This
Userclass will represent the participants in the chat room. Each user has a reference to the chat mediator.Client Code to Use the Mediator: We can now create the
ChatRoommediator and add users to it.Output:
Explanation of the Code
ChatRoom (Mediator): Manages communication between
Userobjects, sending each message to all users except the sender.User (Colleague): Represents a chat user who can send and receive messages. Each user has a reference to the
ChatRoommediator.Client Code: Sets up the mediator and colleagues, adds users to the chat room, and triggers messages to demonstrate interaction.
When to Use the Mediator Pattern
When you have a complex web of communication between multiple objects, and you want to simplify and centralize this communication.
When you want to promote loose coupling and reduce dependencies between objects.
When you need to add or change interaction logic without affecting the communicating classes directly.
Pros and Cons
Pros:
Reduces coupling between colleague classes by centralizing interactions.
Simplifies object communication and can make the code more readable.
Makes it easier to modify communication logic without changing individual colleague classes.
Cons:
The mediator can become a complex "god object" if too much logic is placed in it, which can lead to high maintenance costs.
Can make the code harder to follow if the mediator becomes too complex, obscuring how interactions work.
This example shows how the mediator pattern simplifies the communication between users, keeping each user independent of others and delegating the communication logic to a central mediator.
Memento Pattern
allows an object to save and restore its previous state without violating encapsulation.
The pattern is commonly used in scenarios where you need to implement undo/redo functionality, as it enables an object to return to a previous state without exposing the details of its internal structure.
Key Concepts of the Memento Pattern
Originator: The object whose state needs to be saved and restored. It creates a memento containing a snapshot of its state and can restore itself to a state stored in a memento.
Memento: The memento object represents a snapshot of the
Originator's state. It’s typically an immutable object that only theOriginatorcan modify.Caretaker: Manages the memento's lifecycle by requesting new mementos and storing them. It does not modify or access the contents of the memento directly, maintaining encapsulation.
Example: Text Editor with Undo Feature
In this example, we’ll create a simple text editor where the Editor class (Originator) can save and restore its state through a memento object.
Step-by-Step Code
Define the Memento Class: This class will store the state of the
Editorat a given point.Implement the Originator Class: The
Editorclass is our originator. It can create and restore mementos of its state.Implement the Caretaker: The
Historyclass acts as the caretaker. It stores theEditor’s mementos, enabling undo functionality.Client Code to Use the Memento Pattern: This code demonstrates saving and restoring the editor’s state.
Explanation of the Code
Memento: Represents the
Editor's state at a specific moment, holding a copy of the text content.Editor (Originator): Manages its own state (
_content) and provides methods to save its state to a memento or restore its state from a memento.History (Caretaker): Stores the history of
Editor's mementos, allowing the editor to revert to previous states as needed. It doesn’t access the content of mementos directly.
When to Use the Memento Pattern
When you need to implement undo/redo functionality.
When you want to take snapshots of an object’s state at various points without exposing or breaking its internal structure.
When the internal state of an object changes frequently, and you need to retain previous states for restoration.
Pros and Cons
Pros:
Provides an easy way to implement undo/redo functionality.
Protects the encapsulation of an object’s internal state, as only the originator can modify its own mementos.
Cons:
Can use a lot of memory if the originator's state is large or if many mementos are stored.
Can increase complexity, as the caretaker must manage mementos' lifecycle.
This example demonstrates how the Memento pattern allows an Editor object to restore its state through a simple, encapsulated mechanism. The pattern provides a way to manage the state without exposing it, enabling features like undo/redo without compromising data integrity or encapsulation.
Observer Pattern
defines a subscription mechanism for notifying multiple objects about any events that happen to a particular object, the "subject."
This pattern is commonly used to implement distributed event-handling systems, allowing objects to stay in sync with changes in another object without being tightly coupled to it.
Key Concepts of the Observer Pattern
Subject (Observable): The object being observed. It maintains a list of observers, notifies them of state changes, and allows them to subscribe or unsubscribe.
Observer: An interface or abstract class for objects that want to be notified about changes in the subject. Observers implement an
updatemethod to receive updates.Concrete Observers: These are the classes that implement the
Observerinterface and define how they respond to updates from the subject.Client: Sets up the relationship between the subject and observers.
Example: Weather Station
In this example, we’ll create a simple weather station where different displays (observers) update their data based on changes in temperature, humidity, etc., from a weather station (subject).
Step-by-Step Code
Define the Observer Interface: This interface declares the
updatemethod for observers.Define the Subject (Observable) Interface: This interface declares methods for adding, removing, and notifying observers.
Implement the Concrete Subject: The
WeatherStationclass stores weather data and notifies observers when data changes.Implement Concrete Observers: Define specific observer classes, such as a display for current conditions and another for forecasting.
Client Code to Use the Observer Pattern: Now we can create the
WeatherStationand attach observers to it.Output:
Explanation of the Code
WeatherStation (Subject): Maintains a list of observers and notifies them when weather data changes. The
set_measurementsmethod updates weather data and then callsnotify_observersto push the update to each observer.CurrentConditionsDisplay and ForecastDisplay (Observers): These classes implement the
Observerinterface and define how they respond to weather data updates.Client Code: Sets up the observers and attaches them to the
WeatherStation. When weather data is updated, both displays receive and handle the update.
When to Use the Observer Pattern
When an object needs to notify multiple other objects about changes in its state.
When you want to reduce the coupling between the subject and its observers.
When changes in one object’s state should automatically update other objects, such as in MVC architectures or event-handling systems.
Pros and Cons
Pros:
Promotes loose coupling between subject and observers.
Allows easy addition of new observers without changing the subject’s code.
Supports broadcasting of state changes to multiple observers simultaneously.
Cons:
Can lead to unexpected updates, especially if the subject frequently changes.
Observer management (e.g., adding and removing observers) can become complex if many observers are involved.
Can cause performance issues if there are a large number of observers and frequent updates.
This example demonstrates how the Observer pattern allows a weather station to notify multiple displays of changes in weather data, achieving a loosely coupled relationship between the weather station and its displays.
State Pattern
allows an object to change its behavior when its internal state changes.
This pattern is especially useful when an object has different states that influence how it should respond to certain events.
It’s a way to simplify complex conditional logic, promoting cleaner and more maintainable code.
Key Concepts of the State Pattern
State Interface: Declares the methods that concrete states must implement. This ensures a uniform interface for all states.
Concrete States: Implement the specific behavior associated with each state of the context.
Context: The main object whose behavior changes based on its internal state. It has a reference to the current state and delegates state-specific behavior to it.
Client: Interacts with the context, triggering state transitions indirectly.
Example: Audio Player with Different States (Playing, Paused, Stopped)
In this example, we’ll create a simple audio player that can be in one of three states: Playing, Paused, or Stopped. Each state will have its own behavior for actions like play, pause, and stop.
Step-by-Step Code
Define the State Interface: This interface declares the methods that each state should implement.
Implement Concrete States: Define the behavior for each state (
PlayingState,PausedState,StoppedState).Implement the Context Class: The
AudioPlayerclass will maintain a reference to the current state and delegate actions to it.Client Code to Use the State Pattern: Now we can create the
AudioPlayerand perform state-specific actions.Output:
Explanation of the Code
PlayingState, PausedState, StoppedState (Concrete States): Implement the specific behaviors for each state, defining how the player should respond to
play,pause, andstopactions.AudioPlayer (Context): Maintains a reference to the current state and delegates actions to the state. It changes its state based on the current behavior.
Client Code: Creates an instance of
AudioPlayerand interacts with it, demonstrating the state transitions.
When to Use the State Pattern
When an object’s behavior changes based on its state.
When you want to simplify complex conditional logic based on an object’s state.
When you want to make the object more flexible by allowing behavior changes dynamically.
Pros and Cons
Pros:
Simplifies complex conditional logic, replacing it with state-specific classes.
Promotes the Open/Closed Principle by allowing new states to be added without modifying existing code.
Improves code readability and maintainability by separating state-specific behavior.
Cons:
Increases the number of classes, as each state requires its own class.
Can add complexity to simple systems where only a few states are needed.
This example demonstrates how the State pattern allows an AudioPlayer to handle actions differently depending on its current state, providing a cleaner and more maintainable approach to managing state-dependent behavior.
Strategy Pattern
allows you to define a family of algorithms or behaviors, encapsulate each one as a separate class, and make them interchangeable.
This pattern enables an object to choose the algorithm it uses at runtime, promoting flexibility and avoiding conditional logic.
By encapsulating different behaviors into different classes, the Strategy Pattern provides a way to select the behavior of an object dynamically.
Key Concepts of the Strategy Pattern
Strategy Interface: Defines a common interface for all algorithms. Each strategy implements this interface to ensure they’re interchangeable.
Concrete Strategies: Implement the algorithm defined in the Strategy Interface. Each concrete strategy represents a different way to perform a task.
Context: The main object that needs to perform some task but delegates the actual implementation to one of the strategies. The context maintains a reference to a strategy object, and clients can set or change the strategy dynamically.
Example: Payment System with Different Payment Methods
In this example, we’ll create a payment system where a user can pay using different payment methods, such as a credit card or PayPal. Each payment method represents a different strategy.
Step-by-Step Code
Define the Strategy Interface: This interface declares the
paymethod that all payment strategies must implement.Implement Concrete Strategies: Each payment method (e.g., CreditCardPayment, PayPalPayment) implements the
paymethod defined inPaymentStrategy.Implement the Context Class: The
ShoppingCartclass (context) will use one of the payment strategies to process a payment.Client Code to Use the Strategy Pattern: We can now create different payment strategies and use them with the shopping cart.
Output:
Explanation of the Code
PaymentStrategy (Strategy Interface): Defines the
paymethod that all payment strategies must implement.CreditCardPayment and PayPalPayment (Concrete Strategies): Each class represents a different payment method, implementing the
paymethod in its own way.ShoppingCart (Context): Manages the items and total cost, and uses a payment strategy to process the payment.
Client Code: Creates different payment strategies and sets the strategy in the
ShoppingCartcontext before callingcheckout.
When to Use the Strategy Pattern
When you have multiple ways to perform a task, and you want to make the method easily interchangeable.
When you want to avoid complex conditional statements to select different behaviors.
When the behavior varies based on specific criteria or situations.
Pros and Cons
Pros:
Promotes Open/Closed Principle by allowing new strategies to be added without changing the context.
Provides flexibility to switch between different algorithms or behaviors at runtime.
Reduces conditional logic in the context by delegating tasks to strategy classes.
Cons:
Can lead to an increase in the number of classes, as each algorithm requires its own class.
Adds complexity if there are only a few behaviors, as using separate classes might seem unnecessary.
The context must be aware of the various strategies, and clients must know which strategy to use.
This example demonstrates how the Strategy pattern allows a ShoppingCart to handle payments in multiple ways by switching between different payment strategies dynamically, providing a flexible and maintainable solution for handling different payment methods.
Template Pattern
defines the skeleton of an algorithm in a method, called a template method, which defers some steps to subclasses.
By doing so, it allows subclasses to redefine certain steps of the algorithm without changing its structure.
This pattern is particularly useful when you have a general algorithm that needs to be shared across multiple classes, but you want to allow subclasses to implement specific parts of the algorithm differently.
Key Concepts of the Template Pattern
Template Method: The template method defines the skeleton of the algorithm. It calls other methods that can either be implemented by the subclass or already provided in the parent class.
Abstract Class: The abstract class provides a template method that outlines the workflow and contains methods that can be either concrete or abstract.
Concrete Methods: These are methods implemented in the abstract class that are common for all subclasses.
Abstract Methods: These are methods that must be implemented by the subclasses to provide custom functionality. These methods represent the parts of the algorithm that may vary.
Hooks: Optional methods that can be overridden by subclasses. They may have a default implementation in the abstract class, or be empty methods, allowing subclasses to customize or extend behavior when needed.
Example: Cooking Recipe
Let’s consider a cooking recipe as an example. All recipes follow a basic structure: gather ingredients, cook the ingredients, and serve. However, the cooking method varies depending on the recipe. This is a classic example where the Template Pattern can be applied.
Step-by-Step Code
Define the Abstract Class with the Template Method: The abstract class
Recipedefines the template method,prepare_recipe, which outlines the steps of the cooking process. Theprepare_recipemethod calls other methods, some of which are concrete (likegather_ingredientsandserve), and some are abstract (likecook).Implement Concrete Subclasses: Define subclasses for specific recipes, such as
PastaRecipeandSoupRecipe. Each subclass provides its own implementation for thecookmethod to describe how it cooks the ingredients.Client Code to Use the Template Pattern: Now we can create instances of
PastaRecipeandSoupRecipeand use theprepare_recipemethod to prepare the dishes.Output:
Explanation of the Code
Recipe (Abstract Class): The
prepare_recipemethod defines the template for the cooking process, ensuring that all recipes follow the same steps. It callsgather_ingredients,cook, andservein order. Thecookmethod is abstract, meaning that subclasses must implement it.gather_ingredientsandserveare concrete methods that are common to all recipes.cookis an abstract method, as cooking differs between recipes.
PastaRecipe and SoupRecipe (Concrete Subclasses): These classes provide specific implementations for the
cookmethod. Each recipe has its own way of cooking, but the steps for gathering ingredients and serving remain the same.Client Code: The client creates instances of the
PastaRecipeandSoupRecipeclasses and callsprepare_recipe, which executes the template method and follows the steps defined in the base class.
When to Use the Template Pattern
When you have multiple classes that share a common algorithm but differ in certain steps.
When you want to define the skeleton of an algorithm in a base class, but allow subclasses to provide specific implementations of some steps.
When you want to promote code reuse while allowing flexibility in certain parts of the algorithm.
Pros and Cons
Pros:
Code Reusability: Common parts of the algorithm are implemented once in the base class, avoiding code duplication.
Flexibility: Subclasses can implement their own behavior while reusing common behavior.
Enforces Consistency: The template method ensures that the algorithm is followed in the same order, reducing errors.
Cons:
Increased Coupling: Subclasses are tightly coupled to the template method, meaning that changes to the template method can affect all subclasses.
Complexity: The pattern can lead to a large number of classes if the algorithm has many variations.
Difficulty in Changing the Algorithm's Structure: Once the structure of the algorithm is defined in the template method, it can be difficult to change without affecting all subclasses.
Example Use Cases for the Template Pattern
Workflow management: In systems where multiple workflows share a common structure but differ in specific tasks (e.g., processing different types of documents).
Game development: For game engines that define a general flow (initialize, update, render) but allow specific games to define unique update and rendering logic.
Data processing pipelines: Where multiple types of data processing follow a similar structure, but each data type requires a different processing strategy.
This example demonstrates how the Template Pattern allows a base class to define the overall structure of a process, while subclasses can provide specific implementations for the variable parts of that process. It ensures consistency while allowing flexibility for customization.
Visitor Pattern
allows you to separate an algorithm from the objects it operates on.
It lets you add further operations to objects without modifying their classes. Essentially, the Visitor Pattern enables you to define a new operation (the "visitor") without changing the classes of the elements on which it operates.
The pattern is typically used when you need to perform different operations on a set of objects with different types, but you want to avoid using multiple
if-elseorswitchstatements to handle each case.Key Concepts of the Visitor Pattern
Visitor Interface: The visitor interface defines a
visitmethod for each concrete element class. This method allows the visitor to perform an operation on the element.Concrete Visitor: The concrete visitor implements the visitor interface and provides the specific behavior for each element type.
Element Interface: The element interface defines an
acceptmethod that allows the visitor to "visit" the element. Each concrete element class implements this interface.Concrete Element: The concrete element implements the
acceptmethod, which calls the appropriatevisitmethod on the visitor.Object Structure: The object structure contains a collection of elements and provides a way to traverse them.
Example: Shape Drawing System
Let’s implement a drawing system where we have different shapes (e.g., Circle, Rectangle), and we want to perform various operations (e.g., calculating the area or printing the shape). Instead of modifying the shape classes, we will use the Visitor Pattern to add new operations to these shapes.
Step-by-Step Code
Define the Visitor Interface: The visitor interface will define a visit method for each type of shape.
Define the Element Interface: The element interface will have an
acceptmethod that accepts a visitor.Define Concrete Element Classes: These are the concrete shape classes that implement the
Shapeinterface and theacceptmethod.Define Concrete Visitors: These classes define specific operations to be performed on the shapes. For example, one visitor calculates the area, and another prints the shape’s details.
Client Code to Use the Visitor Pattern: We can now create a collection of shapes, apply the visitors, and print or calculate areas.
Output:
Explanation of the Code
ShapeVisitor (Visitor Interface): Defines
visit_circleandvisit_rectanglemethods for visiting different shape types. Concrete visitors will implement these methods with the specific logic.Shape (Element Interface): This interface requires concrete shape classes (
Circle,Rectangle) to implement theacceptmethod, which will accept a visitor.Circle and Rectangle (Concrete Element Classes): These classes represent different shapes and implement the
acceptmethod. Theacceptmethod allows the object to "accept" a visitor and pass itself to the visitor's correspondingvisit_*method.AreaCalculator and ShapePrinter (Concrete Visitors): These visitors implement the logic for performing specific operations (calculating areas or printing shape details). The
visit_*methods in the visitors handle the operation for each concrete element type.Client Code: Creates instances of shapes and visitors, then uses the
acceptmethod to apply the visitors' operations to the shapes.
When to Use the Visitor Pattern
When you need to perform operations on objects of different classes: If you have a set of different types of objects and want to perform some operation on all of them without modifying the objects themselves, the Visitor Pattern is ideal.
When new operations need to be added to an existing object structure: If you need to add new operations (such as calculating a new property) to an existing set of objects, the Visitor Pattern allows you to do so without modifying the classes themselves.
When you want to avoid adding a lot of conditional logic: The Visitor Pattern allows you to remove complex
if-elseorswitchstatements in favor of polymorphic method dispatch.
Pros and Cons
Pros:
Open for Extension, Closed for Modification: The pattern follows the open/closed principle. New operations can be added without modifying the existing classes (elements).
Separation of Concerns: The operations are separated from the elements, making it easier to maintain and extend the system.
Flexibility: Allows adding new operations to a structure of objects without modifying the objects themselves.
Cons:
Increased Complexity: The pattern introduces additional classes (visitors), which may increase complexity if there are many types of elements or visitors.
Difficult to add new element types: If you want to add a new type of element, you need to modify all visitor classes to handle the new element type, violating the open/closed principle.
Tight Coupling between Visitors and Elements: Visitors are tightly coupled to the element classes. Any change in the element class can require changes in the visitor class.
Example Use Cases for the Visitor Pattern
Compilers/Interpreters: Where different operations need to be performed on various nodes of an abstract syntax tree (e.g., evaluating an expression, printing, optimizing).
GUI Frameworks: When you need to apply different operations to various types of components (e.g., buttons, text fields).
Document Processing: When different operations are required for processing different parts of a document (e.g., images, paragraphs, tables).
In this example, the Visitor Pattern allows us to keep the shape classes (Circle, Rectangle) simple, while allowing us to add new operations (such as area calculation or shape printing) without modifying the shape classes.
Design for Testability Patterns:
3 variants of dependency injection
Fundamental Patterns:
an object handles a request by delegating to a second object (the delegate)
Others
architectural model, assemble different sub-system knowledge to build a solution, AI approach - non gang of four pattern
graphing algorithms - non gang of four pattern
hierarchical state machine - non gang of four pattern
Last updated