The Classic Design Patterns:
Where Are They Now?
Brandon Rhodes
code::dive 2022
Wrocław, Poland
Design Patterns (1994)
Book on Object Oriented programming
by Erich Gamma, Richard Helm,
Ralph Johnson, and John Vlissides
(the ‘Gang of Four’)
It described 23 ‘design patterns’
that had become common solutions for
the problems with Object Oriented languages
The goal for this talk:
To make a brief survey of the 23 patterns
Can we learn anything from the ones that survived?
Can we learn anything from the ones that didn’t?
Limitations of this talk —
But that’s okay!
It’s fine if your list of relevant patterns
is different than mine by the time we finish the talk
The goal is for these old patterns to help
us reflect on how we program today
Happily, the Design Patterns book is online,
so you can review the patterns yourself
later to develop your own opinions
if you don’t like mine
https://www.cs.unc.edu/~stotts/GOF/hires/contfso.htm
Some of my own ideas are also on the web
https://python-patterns.guide/
So here we go
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
First, let’s cross off the patterns that aren’t relevant if your programming language offers first-class functions.
What are first-class functions?
Functions that can be:
• Passed as an argument
• Placed in a data structure
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Factory Method — Subclass Application
, override a method
Not only does that sound hopelessly awkward today, but even Design Patterns itself, back in 1994, offered a better approach! Two chapters earlier: the Abstract Factory.
# The Abstract Factory
class MyDocumentFactory(DocumentFactory):
def build():
return MyDocument()
f = MyDocumentFactory()
a = Application(f)
Q: Why prefer the Abstract Factory to the Factory Method?
A: Because of a fundamental design principle from the Introduction chapter of the Design Patterns book:
Favor object composition over class inheritance.
# What is ‘composition’? You have ‘composed’
# two objects when you give one of them
# a reference to the other:
class MyDocumentFactory(DocumentFactory):
def build():
return MyDocument()
f = MyDocumentFactory() # object 1
a = Application(f) # object 2
Favor object composition over class inheritance.
Why?
Because putting objects together dynamically
at runtime is more flexible than writing
extra classes at compile time
Factory Method? Class inheritance.
Abstract Factory? Object composition.
Abstract Factory > Factory Method
So, why did Design Patterns include
the Factory Method if a better
alternative already existed?
Because they wanted the book to be a complete catalog
of all common Object Oriented patterns—
they did not limit themselves to best practices.
And the Factory Method,
despite its awkwardness,
was in widespread use.
‘Factory methods pervade toolkits and frameworks.
MacApp … ET++ … Unidraw … Smalltalk-80 … Orbix ORB’
Abstract Factory
Factory Method
But neither one is a pattern we use in modern languages
# First-class functions? Pass a function.
def build_document():
return MyDocument()
a = Application(build_document)
# First-class types? Just pass the class!
a = Application(MyDocument)
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Template Method — if Application
needs you to give it three procedures, it should force you to write a subclass
Favor object composition over class inheritance.
These three methods should live on a separate class.
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
The Prototype Pattern
Example: Imagine we are writing a music composition
app that wants to let users create four kinds of object.
# The objects are of different classes.
# Some of their constructors need arguments.
Rest() # quarter rest
Note(1) # whole note
Note(2) # half note
Note(4) # quarter note
How could you explain to, say, a menu widget
how to create these objects when menu items are selected?
# In a modern language, you would
# just pass a data structure
menu.add_actions([
('Rest', Rest),
('Whole note', Note, 1),
('Half note', Note, 2),
('Quarter note', Note, 4),
])
Q: But what if your language isn’t that powerful?
Are you going to need as many factories
as kinds of object you want to create?
build_rest()
build_whole_note()
build_half_note()
build_quarter_note()
A: The Prototype Pattern says, No!
Simply create examples of the four objects,
and give them each a clone()
method
# The Prototype Pattern
r = Rest()
r.clone() → another Rest
n = Note(1)
n.clone() → another Note of length 1
n = Note(2)
n.clone() → another Note of length 1/2
n = Note(4)
n.clone() → another Note of length 1/4
# Thanks to the Prototype Pattern’s `.clone()`
# method, we can give plain object instances
# to the menu instead of factories.
menu.add_action('Rest', Rest())
menu.add_action('Whole note', Note(1))
menu.add_action('Half note', Note(2))
menu.add_action('Quarter note', Note(4))
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Singletons: those annoying global objects that code needs access to but you don’t want to pass everywhere
Do we need Singletons? Sometimes. Maybe.
That’s a big topic of its own.
But, do we need the Singleton Pattern, where the class itself
is contorted to force its use as a Singleton?
No!
# Just write the class normally,
# build a single instance, and provide
# a global name or a global function
# that returns that instance.
import logging
root = logging.getLogger()
Why avoid the Singleton Pattern?
One reason: testing becomes hard and tests
of the singleton become coupled if you really
truly can’t create a fresh instance,
so the tests are having to share.
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
The Strategy Pattern
Example: What if an object needs to be given
a specific paragraph-breaking routine at
runtime, out of 3 that are available?
Strategy Pattern
Put the 3 versions of the routine
in 3 single-method classes, build an
instance of each, and pass them as
constructor arguments at runtime
Strategy Pattern
We can solve this more simply
In any modern language, you solve this problem by just writing 3 functions, and passing one in as an argument.
Done
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
In powerful modern languages, those six patterns disappear because they aren’t needed.
23
- 6
___
17
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
There are two patterns
that are also disappearing
from our code, but it’s because
they are so useful—they are now
getting built into our languages.
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Both of them involve a producer object
which is going to build or retrieve items and
a consumer object that wants to iterate over them
next()
next()
over and over In legacy languages,
you have to choose one approach or the other.
Either way, someone’s class has to
suffer callback-style programming.
Solution: generators
(CLU 1975, Icon 1977, Python 2001, then C#, JS, Ruby)
class Ten: # Before generators,
n = 0 # you had to write up the
# Iterator Pattern yourself
def next(self):
self.n += 1
if self.n > 10:
raise StopIteration
return self.n
def ten(): # After generators
n = 0
while n < 10:
n += 1
yield n
Each time the consumer asks for an item,
the producer runs enough code to reach its
next yield
and deliver the item.
So the consumer’s requests for more
items gradually cause the producer
to run all the way to completion.
# So *both* the producer and consumer
# can be written with normal control flow
# statements like `if` `for` and `while`;
# neither is forced to endure callbacks.
def producer(...):
for item in ...:
yield item
def consumer(...):
for item in producer(...):
... # operate on `item`
# Passing a producer to a consumer works
# exactly as though you had implemented
# the Iterator Pattern by hand.
p = producer()
result = consumer(p)
except—
Don’t actually let a consumer talk to a producer
# If you have the consumer drive the
# producer directly, your main thread
# loses control.
p = producer()
result = consumer(p)
p = producer()
result = consumer(p)
# Instead, run the producer to completion
# and save the output in a plain flat data
# structure: a list.
plain_list = []
for item in producer():
plain_list.append(item)
# Then, pass the list to the consumer.
result = consumer(plain_list)
This idea may sound silly when we’re only
talking about one producer and one consumer, but as
an application grows, you should always prefer
shallow call stacks to deep ones
It’s a good day when you can draw a
bright line across your code and say,
‘everything above this line has finished
running and the only thing crossing this
line is a simple inert data structure’
plain_list = []
for item in producer():
plain_list.append(item)
# -------- bright line --------
result = consumer(plain_list)
Why?
Because data is easier to
reason about than control flow
The Mythical Man-Month by Fred Brooks (1975):
“Show me your flowchart
and conceal your tables,
and I shall continue to be mystified.
Show me your tables,
and I won’t usually need your flowchart;
it’ll be obvious.”
♥ Your Data
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
23
- 8
___
15
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Proxy — sends method calls across the network
Memento — saves object state as a binary string
Observer — object offer subscriptions to its changes
So what are their sins?
Proxy Pattern — hides how data is passed across the network
Memento Pattern — hides how data is persisted to disk
Observer Pattern — scatters data rather than unifying it
♥ Your Data
Proxy Pattern
Network APIs between your services should use
a data format you understand (JSON, Protobuf, CSV)
and be hand-crafted to make sense over
a high-latency channel
Good object methods: small, orthogonal, numerous
Good remote procedures: comprehensive, aggregate, few
Example: a class might offer getters and setters
for all of its 11 instance variables; but a network
call would probably return all 11 at once,
and set as many as you want to provide
Memento Pattern
Data should be persisted in a format that’s
independent of your programming language and
that you can access directly using other tools
(JSON, Protobuf, CSV, MySQL, Postgres)
Observer Pattern
Should we really be building forests of objects
that are subscribed to each other’s state changes?
No!
Look on YouTube for Facebook talks about React.
React is the most popular JS library today
jQuery
React
What is the secret to React’s success?
Clicks, Keystrokes, Server events
↙
State
(data) ↘
View
So, React makes a data structure
the foundation of your application
Proxy Pattern
Memento Pattern
Observer Pattern
All three of these patterns
fail to put data first
♥ Your Data
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
23
-11
___
12
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Bridge — one real-world object, but two classes
Decorator — a wrapper with the same interface as the object
Decorator — a wrapper with the same interface as the object
Mediator — extra class to take action when widgets update
State — replace if-else stacks with separate objects
Bridge Pattern
Decorator Pattern
Mediator Pattern
State Pattern
Q: What do these have in common?
Bridge Pattern
Decorator Pattern
Mediator Pattern
State Pattern
A: They all break the Object Oriented assumption that one real-world object should become one object in your code.
Bridge ← class
Decorator ← Decorator ← class
Mediator ← class
class → State
Bridge Pattern
Decorator Pattern
Mediator Pattern
State Pattern
These four patterns all begin a disassembly of Object Orientation, and start using classes to organize procedures into layers rather than to represent data
data ← objects → actions
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Bridge ← class
Decorator ← Decorator ← class
Mediator ← class
class → State
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Builder Pattern — provides a single object whose API builds and returns an object hierarchy for us
# Example: the matplotlib ‘Axis’ object
# provides methods for building Plot and
# Annotation objects.
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot(x, y, c='red')
ax.annotate(x[0], y[0], 'start')
ax.annotate(x[1], y[1], 'end')
Adapter Pattern —
when a class doesn’t offer the interface you need,
write a wrapper class that does, and have its
methods call the underlying object
Example: Python’s socket.makefile()
method returns an Adapter that looks like a file object but whose read()
and write()
methods really call the socket’s recv()
and send()
methods.
adapter.read() -> socket.recv()
adapter.write() -> socket.send()
Flyweight Pattern — small read-only data can
be shared between object instances.
Glyph Glyph
x 108 x 124
y 10 y 10
height 12 height 12
width 8 width 8
descent 2 descent 2
Glyph Glyph
x 108 x 124
y 10 y 10
↘ ↙
CharData
height 12
width 8
descent 2
Example: the integers −5 through 256
in Python are flyweight objects.
Each time a calculation returns an integer in that range,
it’s the same object that gets returned each time.
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Composite Pattern
Chain of Responsibility Pattern
Command Pattern
Interpreter Pattern
These are what I call the ‘Framework Patterns’
Composite — give all nodes in a hierarchy the same interface
DOM = Document Object Model
HTML:
<div>
<img src="example.jpg">
</div>
JS:
div.tagName → 'DIV'
img.tagName → 'IMG'
div.children.length → 1
img.children.length → 0
Chain of Responsibility — in a Composite UI hierarchy,
pass unprocessed clicks and keystrokes up to your parent
This is universal and called ‘bubbling’ in the browser
Command — represent user actions as a list of
objects that have both do()
and undo()
methods
This is universal in browsers and GUI frameworks
It turns out the Command Pattern
is useful in other contexts!
Example: the Django web framework’s
database migrations system
# Django lets you define what your database
# tables should look like using a declarative
# syntax.
from django.db import models
class Question(models.Model):
text = models.CharField(max_length=200)
pub = models.DateTimeField('date published')
As your models change, Django saves those database migrations using the Command Pattern, so you can both apply each migration to move forward but also ‘undo’ a migration to recover and move back.
v1 CreateModel
CreateModel
AddIndex
v2 AlterModelTable
AddField
v3 RenameField
Interpreter — uses the Composite Pattern to
represent an Abstract Syntax Tree, and make it
executable by giving each node a method.
"n + 1"
┌───┐
│add│
└───┘
↗ ↖
┌───────┐ ┌───────┐
│get `n`│ │value 1│
└───────┘ └───────┘
"n + 1"
┌──────┐
│ add │
│eval()│
└──────┘
↗ ↖
┌───────┐ ┌───────┐
│get `n`│ │value 1│
│eval() │ │eval() │
└───────┘ └───────┘
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
Facade—hide a complex system behind a single API class
Problem: to qualify as a Facade Pattern,
an API must live on only one object, but
real-world APIs almost always use several
Examples: two real-world APIs, jQuery and Pandas.
# jQuery: is it a Facade? No, because each
# query returns a separate object.
var paragraphs = $('p'); // jQuery object
paragraphs.find('b') // another object
# Pandas: is its DataFrame a Facade?
filename = 'transactions.csv'
df = pd.read_csv(filename) # DataFrame
print(df.size)
print(df.columns)
# No, because interacting with a
# specific column returns a `Series`.
name = df['Name'] # Series
amount = df['Amount'] # Series
Facade: 1 object
Real APIs: n objects
Also, the Design Patterns example of a Facade
provides no access to the underlying subsystem.
But real-world APIs are usually happy
to let you access the lower level directly.
# jQuery:
paragraphs[0] # raw browser `Element`!
# Pandas:
amount.values() # raw NumPy array!
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |
So we’ve made it—we’ve completed our survey.
Creational | Structural | Behavioral |
---|---|---|
Abstract Factory | Adapter | Chain of Responsibility |
Builder | Bridge | Command |
Factory Method | Composite | Interpreter |
Prototype | Decorator | Iterator |
Singleton | Facade | Mediator |
Flyweight | Memento | |
Proxy | Observer | |
State | ||
Strategy | ||
Template Method | ||
Visitor |