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.