This post summarizes some design patterns I used in daily work or learnt from books.

Creational Pattern

Factory

  • Define an interface for creating an object.
  • Let subclasses decide which class to instantiate.

from abc import ABCMeta, abstractmethod

class Shape(metaclass=ABCMeta):
    @abstractmethod
    def op(self):
        pass

class Triangle(Shape):
    def op(self):
        print("triangle")

class Circle(Shape):
    def op(self):
        print("circle")

class Factory(metaclass=ABCMeta):
    def __init__(self):
        self.shape = self.getInstance()

    @abstractmethod
    def getInstance(self):
        pass

class ConcreteFactoryA(Factory):
    def getInstance(self):
        return Triangle()

class ConcreteFactoryB(Factory):
    def getInstance(self):
        return Circle()

Abstract Factory

  • Provide an interface for creating families of related objects.
  • Avoid specifying their concrete classes.

from abc import ABCMeta, abstractmethod

class Shape(metaclass=ABCMeta):
    @abstractmethod
    def op(self):
        pass

class Triangle(Shape):
    def op(self):
        print("triangle")

class Color(metaclass=ABCMeta):
    @abstractmethod
    def op(self):
        pass

class Red(Color):
    @abstractmethod
    def op(self):
        print("red")

class Factory(metaclass=ABCMeta):
    def __init__(self):
        self.shape = self.getInstance()

    @abstractmethod
    def getShape(self):
        pass

    @abstractmethod
    def getColor(self):
        pass

class ShapeFactory(Factory):
    def getShape(self):
        return Triangle()

class ColorFactory(Factory):
    def getColor(self):
        return Red()

Builder

  • Separate the construction of a complex object from its representation.

class Director:
    def __init__(self):
        self._builder = None

    def construct(self, builder):
        self._builder = builder
        self._builder.buildPartA()
        self._builder.buildPartB()

class Builder(metaclass=ABCMeta):
    def __init__(self):
        self.product = Product()

    @abstractmethod
    def buildPartA(self):
        pass

    @abstractmethod
    def buildPartB(self):
        pass

class ConcreteBuilder(Builder):
    def buildPartA(self):
        pass

    def buildPartB(self):
        pass

class Product:
    pass

Singleton

  • Ensure a class has only one instance.
  • Provide a global point of access to it.
  • Initialization on first use.

class Singleton(type):
    def __init__(cls, name, bases, attrs, **kwargs):
        super().__init__(name, bases, attrs)
        cls._instance = None

    def __call__(cls, *args, **kwargs):
        if cls._instance is None:
            cls._instance = super().__call__(*args, **kwargs)
        return cls._instance

Prototype

  • Specify the kinds of objects to create using a prototypical instance.
  • Create new objects by copying this prototype.

import copy

class Prototype:
    """
    Example class to be copied.
    """

    pass

def main():
    prototype = Prototype()
    prototype_copy = copy.deepcopy(prototype)

Structural Pattern

Adapter

  • Convert the interface of a class into another interface.
  • Wrap an existing class with a new interface.

from abc import ABCMeta, abstractmethod

class Shape(metaclass=ABCMeta):
    @abstractmethod
    def display(self, x1, y1, x2, y2):
        pass

class Rectangle(Shape):
    def __init__(self):
        self._adaptee = LegacyRectangle()

    def display(self, x1, y1, x2, y2):
        self._adaptee.display(x1, y1, x2-x1, y2-y1)

class LegacyRectangle:
    def display(self, x1, y1, w, h):
        pass

Bridge

  • Decouple an abstraction from its implementation.
  • Beyond encapsulation, to insulation.

from abc import abstractmethod, ABCMeta

class Shape:
    def __init__(self, api):
        self._api = api

    def draw(self):
        self._api.drawCircle()

class DrawAPI(metaclass=ABCMeta):
    @abstractmethod
    def drawCircle(self):
        pass

class RedCircle(DrawAPI):
    def drawCircle(self):
        pass

Composite

  • Compose objects into tree structures to represent whole-part hierarchies.
  • Let clients treat individual objects and compositions of objects uniformly.

from abc import abstractmethod, ABCMeta

class Component(metaclass=ABCMeta):
    @abstractmethod
    def do(self):
        pass

class Composite(Component):
    def __init__(self):
        self._children = set()

    def do(self):
        for child in self._children:
            child.do()

    def add(self, component):
        self._children.add(component)

    def remove(self, component):
        self._children.discard(component)

class Leaf(Component):
    def do(self):
        pass

Decorator

  • Attach additional responsibilities to an object dynamically.
  • Provide a flexible alternative to subclassing for extending functionality.
  • Wrapping a gift, putting it in a box, and wrapping the box.

from abc import abstractmethod, ABCMeta

class Shape(metaclass=ABCMeta):
    @abstractmethod
    def draw(self):
        pass

class ShapeDecorator(Shape, metaclass=ABCMeta):
    def __init__(self, shape):
        self._shape= shape

    @abstractmethod
    def operation(self):
        pass

class RedShapeDecorator(ShapeDecorator):
    def operation(self):
        self._shape.operation()

class Circle(Shape):
    def operation(self):
        pass

def main():
    cc = Circle()
    decorator = RedShapeDecorator(cc)
    decorator.operation()

Facade

  • Provide a unified interface to a set of interfaces in a subsystem.
  • Wrap a complicated subsystem with a simpler interface.

class Facade:
    def __init__(self):
        self._subsystem_1 = Subsystem1()
        self._subsystem_2 = Subsystem2()

    def operation(self):
        self._subsystem_1.operation1()
        self._subsystem_1.operation2()
        self._subsystem_2.operation1()
        self._subsystem_2.operation2()

class Subsystem1:
    def operation1(self):
        pass

    def operation2(self):
        pass

class Subsystem2:
    def operation1(self):
        pass

    def operation2(self):
        pass


def main():
    facade = Facade()
    facade.operation()

Flyweight

  • Use sharing to support large numbers of fine-grained objects.

from abc import ABCMeta, abstractmethod

class ShapeFactory:
    def __init__(self):
        self._circleDict = {}

    def getShape(self, key):
        if key not in self._circleDict:
            circle = Circle()
            self._circleDict[key] = circle

        return self._circleDict[key]


class Shape(metaclass=ABCMeta):
    def __init__(self):
        self.intrinsic_state = None

    @abstractmethod
    def draw(self):
        pass


class Circle(Shape):
    def draw(self):
        pass

def main():
    factory = ShapeFactory()
    concrete = factory.getShape("key")
    concrete.draw()

Proxy

  • Provide a surrogate or placeholder for another object to control access to it.
  • Add a wrapper and delegation to protect the real component from undue complexity.

from abc import ABCMeta, abstractmethod

class Subject(metaclass=ABCMeta):
    @abstractmethod
    def op(self):
        pass

class Proxy(Subject):
    def __init__(self, real_subject):
        self._real_subject = real_subject

    def op(self):
        self._real_subject.op()


class RealSubject(Subject):
    def op(self):
        print("real")

def main():
    real_subject = RealSubject()
    proxy = Proxy(real_subject)
    proxy.op()

Behavioral Pattern

Chain of Responsibility

  • Give more than one object a chance to handle the request.
  • Launch-and-leave requests with a single processing pipeline.

class Handler(metaclass=ABCMeta):
    def __init__(self, next=None):
        self._next = next
        self._canHandle = True

    @abstractmethod
    def handle(self):
        pass

class HandlerOne(Handler):
    def handle_request(self):
        if self.canHandle:
            pass
        elif self._next:
            self._next.handle()

class HandlerTwo(Handler):
    def handle(self):
        if self._canHandle:
            pass
        elif self._next:
            self._next.handle()

def main():
    handler1 = HandlerOne()
    handler2 = HandlerTwo(handler1)
    handler2.handle()

Command

  • Encapsulate a request as an object.
  • Let you parametrize clients with different requests.

class Invoker:
    def __init__(self):
        self._commands = []

    def add(self, command):
        self._commands.append(command)

    def call(self):
        for command in self._commands:
            command.execute()

class Command(metaclass=ABCMeta):
    def __init__(self, receiver):
        self._receiver = receiver

    @abstractmethod
    def execute(self):
        pass

class ConcreteCommand(Command):
    def execute(self):
        self._receiver.action()

class Receiver:
    def action(self):
        pass

def main():
    receiver = Receiver()
    cmd = ConcreteCommand(receiver)
    invoker = Invoker()
    invoker.add(cmd)
    invoker.call()

Mediator

  • Define an object that encapsulates how a set of objects interact.
  • Design an intermediary to decouple many peers.

from abc import ABCMeta, abstractmethod

class Mediator(metaclass=ABCMeta):
    def __init__(self):
        self._colleagues = {}

    @abstractmethod
    def register(self, colleague):
        pass

    @abstractmethod
    def op(self):
        pass

class ConcreteMediator(Mediator):
    def register(self, colleague):
        self._colleagues.append(colleague)
        colleague.setMeidator(self)

class Colleague(metaclass=ABCMeta):
    def __init__(self, mediator=None):
        self._mediator = mediator

    def setMediator(self, mediator):
        self._mediator = mediator

    @abstractmethod
    def op(self):
        pass

class ColleagueA:
    def op(self):
        pass

class ColleagueB:
    def op(self):
        pass

def main():
    c1 = ColleagueA()
    c2 = ColleagueB()
    mediator = ConcreteMediator()
    mediator.register(c1)
    mediator.register(c2)

    c1.op()
    c2.op()

Memento

  • Capture and externalize an object’s internal state.
  • Promote undo or rollback to full object status.

class Memnto:
    def __init__(self, state=None):
        self._state = state

    def getState(self):
        return self._state

class Originator:
    def __init__(self, state=None):
        self._state = state

    def setState(self, state):
        self._state = state

    def getStateFromMemento(self, memento):
        self._state = memento.getState()

    def createMemento(self):
        return Memnto(self._state)

class CareTaker:
    def __init__(self):
        self._mem = []

    def add(self, state):
        self._mem.append(state)

    def get(self, index):
        if index < len(self._mem):
            return self._mem[index]
def main():
    originator = Originator()
    taker = CareTaker()

    originator.setState(True)
    taker.add(originator.createMemento())

    originator.getStateFromMemento(taker.get(0))

Observer

  • Define a one-to-many dependency between objects.
  • When one object changes state, all its dependents are notified and updated.

class Subject:
    def __init__(self):
        self._state = None
        self._obList = []

    def attach(self, observer):
        observer._subject = self
        self._obList.add(observer)

    def detach(self, observer):
        observer._subject = None
        self._obList.discard(observer)

    def _notify(self):
        for observer in self._obList:
            observer.update(self._state)

    def updateState(self, state):
        self._state = state
        self._notify()

class Observer(metaclass=ABCMeta):
    def __init__(self):
        self._subject = None
        self._state = None

    @abstractmethod
    def update(self, arg):
        pass

class ConcreteObserver(Observer):
    def update(self, state):
        self._state = state

def main():
    subject = Subject()
    observer =  ConcreteObserver()
    subject.attach(observer)
    subject.updateState(123)

State

  • Allow an object to alter its behavior when its internal state changes.
  • Wrapper + polymorphic wrappee + collaboration.

class Context:
    def __init__(self, state):
        self._state = state

    def request(self):
        self._state.handle()

class State(metaclass=ABCMeta):
    @abstractmethod
    def handle(self):
        pass

class ConcreteStateA(State):
    def handle(self):
        pass

class ConcreteStateB(State):
    def handle(self):
        pass

def main():
    state = ConcreteStateA()
    context = Context(state)
    context.request()

Strategy

  • Define a family of algorithms, encapsulate each one.
  • Capture the abstraction in an interface, bury implementation details in derived classes.

class Context:
    def __init__(self, strategy):
        self._strategy = strategy

    def algorithm(self):
        self._strategy.algorithm()


class Strategy(metaclass=ABCMeta):
    @abstractmethod
    def algorithm(self):
        pass

class ConcreteStrategyA(Strategy):
    def algorithm(self):
        pass

class ConcreteStrategyB(Strategy):
    def algorithm(self):
        pass

def main():
    strategy = ConcreteStrategyA()
    context = Context(strategy)
    context.algorithm()

Visitor

  • Represent an operation to be performed on the elements of an object structure.
  • Define a new operation without changing the classes of the elements on which it operates.

class Element(metaclass=ABCMeta):
    @abstractmethod
    def accept(self, visitor):
        pass

class ConcreteElementA(Element):
    def accept(self, visitor):
        visitor.visitA(self)

class ConcreteElementB(Element):
    def accept(self, visitor):
        visitor.visitB(self)

class Visitor(metaclass=ABCMeta):
    @abstractmethod
    def visitA(self, eleA):
        pass

    @abstractmethod
    def visitB(self, eleB):
        pass

class ConcreteVisitorA(Visitor):
    def visitA(self, eleA):
        pass

    def visitB(self, eleB):
        pass

def main():
    visitor = ConcreteVisitorA()
    element = ConcreteElementA()
    element.accept(visitor)

Reference