Design Patterns

Gang of Four Design Patterns

Singleton Pattern

 classDiagram
 class Singleton {
  -instance
  +getInstance()
  -Singleton()
 }

The Singleton pattern ensures a class has only one instance and provides a global point of access to it. It is useful for managing shared resources like configuration settings or logging.

Factory Method Pattern

 classDiagram
 class Creator {
  +factoryMethod()
  +anOperation()
 }
 class ConcreteCreator {
  +factoryMethod()
 }
 class Product
 class ConcreteProduct
 Creator <|-- ConcreteCreator
 Product <|-- ConcreteProduct

The Factory Method pattern defines an interface for creating an object but lets subclasses decide which class to instantiate. This promotes loose coupling by deferring instantiation to subclasses.

Abstract Factory Pattern

 classDiagram
 class AbstractFactory {
  +createProductA()
  +createProductB()
 }
 class ConcreteFactory1 {
  +createProductA()
  +createProductB()
 }
 class ConcreteFactory2 {
  +createProductA()
  +createProductB()
 }
 class AbstractProductA
 class AbstractProductB
 class ProductA1
 class ProductB1
 class ProductA2
 class ProductB2
 AbstractFactory <|-- ConcreteFactory1
 AbstractFactory <|-- ConcreteFactory2
 AbstractProductA <|-- ProductA1
 AbstractProductA <|-- ProductA2
 AbstractProductB <|-- ProductB1
 AbstractProductB <|-- ProductB2

The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes. This pattern helps keep your code independent from the creation and representation of objects.

Builder Pattern

 classDiagram
 class Director {
  +construct(builder)
 }
 class Builder {
  +buildPartA()
  +buildPartB()
  +getResult()
 }
 class ConcreteBuilder {
  +buildPartA()
  +buildPartB()
  +getResult()
 }
 Director --> Builder
 Builder <|-- ConcreteBuilder

The Builder pattern separates the construction of a complex object from its representation. This allows the same construction process to create different representations, making it easier to build complex objects step by step.

Prototype Pattern

 classDiagram
 class Prototype {
  +clone()
 }
 class ConcretePrototype1 {
  +clone()
 }
 class ConcretePrototype2 {
  +clone()
 }
 Prototype <|-- ConcretePrototype1
 Prototype <|-- ConcretePrototype2

The Prototype pattern creates new objects by copying an existing object (the prototype). It’s particularly useful when object creation is costly, and cloning an object is more efficient than building one from scratch.

Adapter Pattern

 classDiagram
 class Target {
  +request()
 }
 class Adapter {
  +request()
 }
 class Adaptee {
  +specificRequest()
 }
 Target <|.. Adapter
 Adapter --> Adaptee

The Adapter pattern allows incompatible interfaces to work together by converting one interface into another expected by the client. It’s often used to integrate classes that couldn’t otherwise collaborate.

Bridge Pattern

 classDiagram
 class Abstraction {
  +operation()
 }
 class RefinedAbstraction {
  +operation()
 }
 class Implementor {
  +operationImpl()
 }
 class ConcreteImplementorA {
  +operationImpl()
 }
 class ConcreteImplementorB {
  +operationImpl()
 }
 Abstraction --> Implementor
 Abstraction <|-- RefinedAbstraction
 Implementor <|-- ConcreteImplementorA
 Implementor <|-- ConcreteImplementorB

The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently. It is especially useful when both the abstractions and their implementations need to be extensible by subclassing.

Composite Pattern

 classDiagram
 class Component {
  +operation()
 }
 class Leaf {
  +operation()
 }
 class Composite {
  +operation()
  +add(Component)
  +remove(Component)
  +getChild(int)
 }
 Component <|-- Leaf
 Component <|-- Composite
 Composite --> Component

The Composite pattern composes objects into tree structures to represent part-whole hierarchies. It allows clients to treat individual objects and compositions uniformly.

Decorator Pattern

 classDiagram
 class Component {
  +operation()
 }
 class ConcreteComponent {
  +operation()
 }
 class Decorator {
  +operation()
 }
 class ConcreteDecorator {
  +operation()
  +addedBehavior()
 }
 Component <|-- ConcreteComponent
 Component <|-- Decorator
 Decorator <|-- ConcreteDecorator
 Decorator o-- Component

The Decorator pattern dynamically adds responsibilities to an object without modifying its structure. It is a flexible alternative to subclassing for extending functionality.

Facade Pattern

 classDiagram
 class Facade {
  +operation()
 }
 class SubsystemA {
  +operationA()
 }
 class SubsystemB {
  +operationB()
 }
 class SubsystemC {
  +operationC()
 }
 Facade --> SubsystemA
 Facade --> SubsystemB
 Facade --> SubsystemC

The Facade pattern provides a simplified interface to a complex subsystem. By encapsulating the subsystem’s complexities, it makes the system easier to use and understand.

Flyweight Pattern

classDiagram
 class Flyweight {
  +operation()
 }
 class ConcreteFlyweight {
  +operation()
 }
 class FlyweightFactory {
  +getFlyweight(key)
 }
 Flyweight <|-- ConcreteFlyweight
 FlyweightFactory o-- Flyweight

The Flyweight pattern minimizes memory usage by sharing as much data as possible with similar objects. It is particularly effective when you have a large number of objects with shared state.

Proxy Pattern

classDiagram
 class Subject {
  +request()
 }
 class RealSubject {
  +request()
}
 class Proxy {
  +request()
 }
 Subject <|.. Proxy
 Proxy --> RealSubject

The Proxy pattern provides a surrogate to control access to another object. It can add functionalities such as lazy loading, caching, or logging before forwarding requests to the real subject.

Chain of Responsibility Pattern

classDiagram
 class Handler {
  +handleRequest()
  +setNext(Handler)
 }
 class ConcreteHandler1 {
  +handleRequest()
 }
 class ConcreteHandler2 {
  +handleRequest()
 }
 Handler <|-- ConcreteHandler1
 Handler <|-- ConcreteHandler2
 ConcreteHandler1 --> Handler : next

The Chain of Responsibility pattern passes a request along a chain of handlers. Each handler decides either to process the request or pass it to the next handler, promoting loose coupling between sender and receiver.

Command Pattern

classDiagram
 class Command {
  +execute()
 }
 class ConcreteCommand {
  +execute()
 }
 class Receiver {
  +action()
 }
 class Invoker {
  +setCommand(Command)
  +invoke()
 }
 Command <|-- ConcreteCommand
 ConcreteCommand --> Receiver
 Invoker --> Command

The Command pattern encapsulates a request as an object, allowing you to parameterize clients with different requests, queue or log requests, and support undoable operations.

Interpreter Pattern

classDiagram
 class AbstractExpression {
  +interpret(context)
 }
 class TerminalExpression {
  +interpret(context)
 }
 class NonterminalExpression {
  +interpret(context)
 }
 AbstractExpression <|-- TerminalExpression
 AbstractExpression <|-- NonterminalExpression

The Interpreter pattern defines a representation for a language’s grammar along with an interpreter that uses the representation to interpret sentences in the language. It is useful for designing simple domain-specific languages.

Iterator Pattern

classDiagram
 class Aggregate {
  +createIterator()
 }
 class ConcreteAggregate {
  +createIterator()
  +getItem(int)
 }
 class Iterator {
  +first()
  +next()
  +isDone()
  +currentItem()
 }
 Aggregate <|-- ConcreteAggregate

The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. This simplifies traversal through complex data structures.

Mediator Pattern

classDiagram
 class Mediator {
  +notify(sender, event)
 }
 class ConcreteMediator {
  +notify(sender, event)
 }
 class Colleague {
  +send(event)
  +receive(event)
 }
 class ConcreteColleague {
  +send(event)
  +receive(event)
 }
 Mediator <|-- ConcreteMediator
 Colleague <|-- ConcreteColleague
 ConcreteColleague --> Mediator

The Mediator pattern defines an object that encapsulates how a set of objects interact. It centralizes communication between related objects, reducing dependencies and promoting loose coupling.

Memento Pattern

classDiagram
 class Originator {
  +createMemento()
  +setMemento(Memento)
  +getState()
 }
 class Memento {
  -state
  +getState()
 }
 class Caretaker {
  +setMemento(Memento)
  +getMemento()
 }
 Originator --> Memento
 Caretaker --> Memento

The Memento pattern captures an object’s internal state without violating encapsulation, so that the object can be restored to that state later. It is commonly used to implement undo functionality.

Observer Pattern

classDiagram
 class Subject {
  +attach(Observer)
  +detach(Observer)
  +notify()
}
 class Observer {
  +update()
 }
 class ConcreteSubject {
  +notify()
 }
 class ConcreteObserver {
  +update()
 }
 Subject <|-- ConcreteSubject
 Observer <|-- ConcreteObserver
 ConcreteSubject --> Observer

The Observer pattern establishes a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically. It’s widely used in event handling systems.

State Pattern

classDiagram
 class Context {
  +request()
  -state
  +setState(State)
 }
 class State {
  +handle(Context)
 }
 class ConcreteStateA {
  +handle(Context)
 }
 class ConcreteStateB {
  +handle(Context)
 }
 Context --> State
 State <|-- ConcreteStateA
 State <|-- ConcreteStateB

The State pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class as it switches between different states.

Strategy Pattern

classDiagram
 class Context {
  +setStrategy(Strategy)
  +executeStrategy()
 }
 class Strategy {
  +algorithmInterface()
 }
 class ConcreteStrategyA {
  +algorithmInterface()
 }
 class ConcreteStrategyB {
  +algorithmInterface()
 }
 Context --> Strategy
 Strategy <|-- ConcreteStrategyA
 Strategy <|-- ConcreteStrategyB

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It allows the algorithm to vary independently from the clients that use it.

Template Method Pattern

classDiagram
 class AbstractClass {
  +templateMethod()
  +primitiveOperation1()
  +primitiveOperation2()
 }
 class ConcreteClass {
  +primitiveOperation1()
  +primitiveOperation2()
 }
 AbstractClass <|-- ConcreteClass

The Template Method pattern defines the skeleton of an algorithm in a method, deferring some steps to subclasses. This lets subclasses redefine certain steps without changing the overall algorithm’s structure.

Visitor Pattern

classDiagram
 class Visitor {
  +visitConcreteElementA(ConcreteElementA)
  +visitConcreteElementB(ConcreteElementB)
 }
 class ConcreteVisitor {
  +visitConcreteElementA(ConcreteElementA)
  +visitConcreteElementB(ConcreteElementB)
 }
 class Element {
  +accept(Visitor)
}
 class ConcreteElementA {
  +accept(Visitor)
  +operationA()
 }
 class ConcreteElementB {
  +accept(Visitor)
  +operationB()
 }
 Visitor <|-- ConcreteVisitor
 Element <|-- ConcreteElementA
 Element <|-- ConcreteElementB
 ConcreteElementA --> Visitor
 ConcreteElementB --> Visitor

The Visitor pattern represents an operation to be performed on the elements of an object structure. It lets you define new operations without changing the classes of the elements on which it operates.