Design Patterns Cheat Sheets

design pattern architect cheat sheet tl;dr

The Structure images for patterns come from refatorguru, and rebuilt by drawio.

Creational

Abstract Factory

Abstract Factory: Provides an interface for creating families of related or dependent objects without specifying their concrete classes.

ProductA
ProductB
Concrete
ProductA1
Concrete
ProductB1
Concrete
ProductA2
Concrete
ProductB2
<<interface>>
AbstractFactory
ConcreteFactory1
...
ConcreteFactory2
...
Client
- factory:AbstractFactory
ProductA pa = factory.createProcuctA()
return new ConcreteProductA2()
+ createProductA():ProductA
+ createProductB():ProductB
+ createProductA():ProductA
+ createProductB():ProductB
+ Client(f:AbstractFactory)
+ someOperation()
+ createProductB():ProductB
+ createProductA():ProductA

Factory

Factory: Defines an interface for creating an object but let subclasses decide which class to instantiate.

Product p = createProcuct()
p.doStuff()
return new ConcreteProductA()
<<interface>>
Product
+ doStuff()
+ someOpeartion()
+ createProduct():Product
Creator
...
Concrete
ProductA
Concrete
ProductB
ConcreteCreatorA
...
ConcreteCreatorB
...
+ createProduct():Product
+ createProduct():Product

Builder

Builder: declares product construction steps that are common to all types of builders.

    builder.reset()
if (type == "simple") {
builder.buildStepA()
} else {
    builder.buildStepB()
    builder.buildStepC()
}
<<interface>>
Creator
+ reset()
+ buildStepA()
+ buildStepB()
+ buildStepC()
Concrete
Builder1
+ reset()
+ buildStepA()
+ buildStepB()
+ buildStepC()
Concrete
Builder2
+ reset()
+ buildStepA()
+ buildStepB()
+ buildStepC()
- result:Product1
- result:Product2
Director
- builder:Builder
+ Director(builder)
+ changeBuilder(builder)
+ make(type)
result = new Product2()
result.setFeatureB()
return this.result
+ getResult():Product2
+ getResult():Product1
b = new ConcreteBuilder1()
d = new Director(b)
d.make()
Product1 p = b.getResult()
client
Product1
Product2

Singleton

Singleton: Ensure a class only has one instance and provide a global point of access to it.

<<interface>>
ServiceInterface
+ operation()
Service
...
+ operation()
Proxy
- realService:Service
+ Proxy(s:Service)
+ checkAccess()
Client
if (checkAccess()) {
    realService.operation()
}
realService = s
+ operation()

Prototype

Prototype: Specifies the kinds of objects to create using a prototypical instance and create new objects by copying this prototype.

<<interface>>
Prototype
+ clone():Prototype
+ getColor():String
button = new Button(10, 40, "red")
registry.addItem("LandingButton", button)
PrototypeRegistry
+ addItem(id:String, p:Prototype)
- items:Prototype[]
+ getById(id:String):Prototype
+ getByColor(color:String):Prototype
Button
+ Button(x, y, color)
- x, y, color
+ Button(prototype)
+ getColor():String
+ clone():Prototype
client
button = registry.getByColor("red")
foreach(item in items)
if (item.getColor() == color)
    return item.clone()
return new Button(this)

Structural

Bridge

Bridge: Depart implementations of different dimension to decouple the abstraction, avoid creating a bunch of subclasses.

Abstraction
- i:Implementation
+ feature1()
+ feature2()
<<interface>>
Implementation
+ method1()
+ method2()
+ method3()
Refined Abstraction
...
+ featureN()
Concrete
Implementations
Concrete
Implementations
optional
Client
i.methodN()
i.methodM()
i.method2()
i.method3()
i.method1()
abstraction.feature1()

Decorator

Decorator: Attaches additional responsibilities to an object dynamically.





Concrete
Component
...
+ execute()
+ extra()
<<interface>>
Component
+ execute()
Concrete
Component
...
+ execute()
Base Decorator
- wrappee:Component
+ BaseDecorator(c:Component)
+ execute()
Client
a = new ConcreteComponent()
b = new ConcreteDecorator1(a)
c = new ConcreteDecorator2(b)
c.execute()
// Decorator ->Decorator ->Component
super::execute
extra()
wrappee.execute()

Adapter

Adapter: Adapt real services to the another service adopted by the client using aggregation or inheritance.

  • Composition:

Client
<<interface>>
Client Interface
+ method(data)
Adapter
- adaptee:Service
+ method(data)
Service
...
+ serviceMethod(specialDta)
specialData = convertToServiceFormat(data)
return adaptee.serviceMethod(specialData)

  • Inheritance:

Client
<<interface>>
Client Interface
+ method(data)
Adapter
- adaptee:Service
+ method(data)
Service
...
+ serviceMethod(specialDta)
specialData = convertToServiceFormat(data)
return adaptee.serviceMethod(specialData)

Composite

Composite: Lets you compose objects into tree structures and then work with these structures as if they were individual objects.

<<interface>>
Component
+ execute()
Leaf
...
+ execute()
Base Decorator
- children:Component[]
+ add(c:Component)
+ execute()
Client
Do some work
+ remove(c:Component)
+ getChildren():Component[]
Delegate all work to children

Facade

Facade: The Facade provides convenient access to a particular part of the subsystem’s functionality. It knows where to direct the client’s request and how to operate all the moving parts.

Subsystem
class
Subsystem
class
Subsystem
class
Additional
Facade
Facade
- linksToSubsystemObjects
- optionalAddtionalFacade
Client
+ subsystemOperation()
Subsystem
class
Subsystem
class
Subsystem
class
Subsystem
class
Subsystem
class
...
+ anotherOperation()

Flyweight

Flyweight: is a structural design pattern that lets you fit more objects into the available amount of RAM by sharing common parts of state between multiple objects instead of keeping all of the data in each object.

Context
- uniqueState
- flyweight
+ Context(repeatingState, uniqueState)
+ operation()
this.uniqueState = uniqueState
this.flyweight =
factory.getFlyweight(repeatingState)
flyweight.operation(uniqueState)
FlyweightFactory
- cache:Flyweight[]
+ getFlyweight(repeatingState)
Flyweight
- repeatingState
+ operation(uniqueState)
Client
if (cache[repeatingState] == null) {
    cache[repeatingState] =
    new Flyweight(operatingState)
}

return cache[repeatingState]

Proxy

Proxy: Lets you provide a substitute or placeholder for another object. A proxy controls access to the original object, allowing you to perform something either before or after the request gets through to the original object.

<<interface>>
ServiceInterface
+ operation()
Service
...
+ operation()
Proxy
- realService:Service
+ Proxy(s:Service)
+ checkAccess()
Client
if (checkAccess()) {
    realService.operation()
}
realService = s
+ operation()

Behavioral

Chain of Responsibility

Chain of Responsibility: is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.

<<interface>>
Handler
+ setNext(h:Handler)
+ handle(request)
BaseHandler
- next:Handler
+ setNext(h:Handler)
+ handle(request)
ConcreteHandler
...
+ handle(request)
ConcreteHandler
...
+ handle(request)
Client
h1 = new HandlerA()
h2 = new HandlerB()
h3 = new HandlerC()
h1.setNext(h2)
h2.setNext(h3)
// ...
h1.handle(request)
if (next != null)
    next.handle(request)
if (canHandle(request)) {
    // ...
} else {
    parent::handle(request)
}

Command

Command: is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you pass requests as a method arguments, delay or queue a request’s execution, and support undoable operations.

Command1
- receiver
+ Command1(receiver, params)
+ execute()
<<interface>>
Command
- params
Command2
...
+ execute()
Invoker
+ setCommand(command)
+ executeCommand()
- command
Client
Receiver
...
+ execute()
copy = new CopyCommand(editor)
button.setCommand(copy)
receiver.operation(params)

Iterator

Iterator: Lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.).

<<interface>>
Iterator
+ getNext()
+ hasNext():bool
<<interface>>
Iterable
+ createIterator():Iterator
ConcreteIterator
- iterable:ConcreteIterable
+ ConcreteIterator(Iterable)
+ getNext()
- iterateState
+ hasNext():bool
ConcreteIterable
...
...
+ createIterator():Iterator
Client

Observer

Observer: Lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.

Concrete
Subscribers
...
+ update()
Publisher
- subscribers:Subscriber[]
- mainState
+ subscribe(s:Subscriber)
+ unsubscribe(s:Subscriber)
+ notifySubscribers()
+ mainBizLog()
mainState = newState
notifySubscribers()
Client
<<interface>>
Subscriber
s = new ConcreteSubscriber()
publisher.subscribe(s)
+ update(context)
Concrete
Subscribers
...
+ update()
foreach (s in subscribers)
    s.update(this)

State

State: Lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.

Concrete
State
+ setContext(context)
- context
+ doThis()
+ doThat()
Context
- state
+ Context(initialState)
+ changeState(state)
+ doThis()
+ doThat()
this.state = state
state.setContext(this)
Client
<<interface>>
State
initialState = new ConcreteState()
context = new Context(initialState)
context.doThis()
// Current state may have been
// changed by context or the state
// object self.
+ doThis()
Concrete
State
+ setContext(context)
state.doThis()
- context
+ doThis()
+ doThat()
+ doThat()
// A state may issue state
// transition in context
state = new OtherState()
context.changeState(state)

Strategy

Strategy: Lets you define a family of algorithms, put each of them into a separate class, and make their objects interchangeable.

Concrete
Strategies

+ execute(data)
Context
- strategy
+ doSomething()
this.state = state
state.setContext(this)
Client
<<interface>>
Strategy
str = new SomeStrategy()
context.setStrategy(str)
context.doSomething()
// ...
other = new OtherStrategy()
context.setStrategy(other)
context.doSomething()
Concrete
Strategies

+ execute(data)
+ execute(data)
+ setStrategy(strategy)

Template Method

Template Method: Defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

AbstractClass
...
+ templateMethod()
step1()
if (step2()) {
    step3()
} else {
    step4()
}
+ step1()
+ step2()
+ step3()
+ step4()
ContreteClass1
...
+ step3()
+ step4()
ContreteClass2
...
+ step1()
+ step2()
+ step3()
+ step4()

Memento

Memento: Lets you save and restore the previous state of an object without revealing the details of its implementation.

  • Implementation based on nested classes

m = originator.save()
history.push(m)
// originator.change()
m = history.pop()
originator.restore(m)
Originator
- state
Memento
Caretaker
- state
- history:Memento[]
- originator
- Memento(state)
- getState()
+ undo()
+ doSomething()
+ restore(m:Memento)
+ save():Memento

  • Implementation based on an intermediate interface

cm = (ConcreteMemento) m
state = cm.getState()
Originator
- state
Memento
Client
- state
- history:Memento[]
- originator
- Memento(state)
- getState()
+ undo()
+ restore(m:Memento)
+ save():Memento
<<interface>>
Memento

  • Implementation with even stricter encapsulation

cm = (ConcreteMemento) m
state = cm.getState()
Originator
- state
Memento
Caretaker
- state
- history:Memento[]
+ undo()
+ setState(state)
+ save():Memento
<<interface>>
Memento
+ restore()
<<interface>>
Originator
save():Memento
- Originator
+ ConcreteMemento(originator, state)
+ restore()

Visitor

Visitor: Lets you separate algorithms from the objects on which they operate.

...
ConcreteVisitors
+ visit(e:ElementA)
+ visit(e:ElementB)
element.accept(new ConcreteVisitor())
// Visitor methods know the
// concrete type of the
// element it works with
e.featureB()
Client
...
+ visit(e:ElementA)
<<interface>>
Visitor
+ visit(e:ElementB)
+ featureB()
+ featureA()
+ accept(v:Visitor)
<<interface>>
Element
ElementA
ElementB
...
+ accept(v:Visitor)
+ accept(v:Visitor)
...
ConcreteVisitors
+ visit(e:ElementA)
+ visit(e:ElementB)

Mediator

Mediator: Lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.

<<interface>>
Mediator
ComponentA
ConcreteMediator
- componentA
- componentB
- componentC
- componentD
+ notify(sender)
+ notify(sender)
+ reatOnA()
+ reatOnB()
+ reatOnC()
+ reatOnD()
- m:Mediator
+ operationC()
ComponentA
- m:Mediator
+ operationC()
ComponentA
- m:Mediator
+ operationC()
ComponentA
- m:Mediator
+ operationC()
if (sender == componentA)
    reatOnA()
m.notify(this)