1
17-214
Principles of Software Construction:
Objects, Design, and Concurrency
The “Gang of Four” Design Pattern Tour
Charlie Garrod Chris Timperley
2
17-214
Administrivia
No recitation this week
Homework 6 checkpoint due at the end of today
Parallel implementation due next Wednesday
3
17-214
Unfinished Business
4
17-214
5
17-214
Control deployments at run-time using feature flags
https://martinfowler.com/articles/feature-toggles.html
https://docs.microsoft.com/en-us/azure/devops/migrate/phase-features-with-feature-flags?view=azure-devops
6
17-214
Warning! Feature flags can be dangerous
In laymen’s terms, Knight Capital Group realized a $460 million
loss in 45-minutes. Remember, Knight only has $365 million in
cash and equivalents. In 45-minutes Knight went from being the
largest trader in US equities and a major market maker in the
NYSE and NASDAQ to bankrupt.
https://dougseven.com/2014/04/17/knightmare-a-devops-cautionary-tale/
7
17-214
Summary
DevOps brings development and operations together
Automation, Automation, Automation
Infrastructure as code
Release management
Versioning and branching strategies
Continuous deployment is increasingly common
Exploit opportunities of continuous deployment; perform
testing in production and quickly rollback
Experiment, measure, and improve
8
17-214
Today: A tour of the 23 “Gang of Four” patterns
1994
9
17-214
What patterns have we discussed in class?
10
17-214
How did you use those design patterns?
Strategy
Template Method
Façade
Iterator
Adapter
Composite
Decorator
Observer
Marker interface
Different forms of
factories
Concurrency patterns as
producer-consumer,
thread pool
...
11
17-214
Why learn the Gang of Four design patterns?
Seminal and canonical list of well-known patterns
Patterns that have stood the test of time!
Not all patterns are commonly used
Does not cover all popular patterns
At least know where to look up when somebody mentions the
“Bridge pattern”
12
17-214
Today: A tour of the “Gang of Four” patterns
1. Creational Patterns
2. Structural Patterns
3. Behavioral Patterns
13
17-214
14
17-214
[Pattern Name]
Intent – the aim of this pattern
Use case – a motivating example
Key types – the types that define pattern
Italic type name indicates abstract class; typically this is an interface when
the pattern is used in Java
JDK – example(s) of this pattern in the JDK
15
17-214
I. Creational Patterns
1. Abstract factory
2. Builder
3. Factory method
4. Prototype
5. Singleton
16
17-214
Problem: Build a GUI that supports multiple platforms
The rest of our code should be platform independent!
17
17-214
public interface Window {
private void setTitle(String title);
private void repaint();
}
public class MSWindow implements Window {
...
}
public class MacOSXWindow implements Window {
...
}
Hide platform-specific details behind an interface
How can we write code that will create the correct Window for
the correct platform, without using conditionals?
18
17-214
Abstract Factory Pattern
19
17-214
public class GUIBuilder {
public void buildWindow(AbstractWidgetFactory widgetFactory) {
Window window = widgetFactory.createWindow();
window.setTitle("New Window");
20
17-214
1. Abstract Factory
Intent: allow creation of families of related objects
independent of implementation
Use case: look-and-feel in a GUI toolkit
Each L&F has its own windows, scrollbars, etc.
Key types: Factory with methods to create each family
member, Products
JDK: not common
21
17-214
Problem: Allow the construction to complex objects
public class User {
private final String firstName; // required
private final String lastName; // required
private final int age; // optional
private final String address; // optional
private final String phone; // optional
...
}
How can we handle all combinations of fields when constructing
User objects?
22
17-214
One solution...
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, “”);
}
public User(String firstName, String lastName, int age, String address) {
this(firstName, lastName, age, address, “”);
}
public User(String firstName, String lastName, int age, String address, String phone) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.phone = phone;
}
What are the problems with this solution?
23
17-214
One solution...
public User(String firstName, String lastName) {
this(firstName, lastName, 0);
}
public User(String firstName, String lastName, int age) {
this(firstName, lastName, age, “”);
}
public User(String firstName, String lastName, int age, String address) {
this(firstName, lastName, age, address, “”);
}
public User(String firstName, String lastName, int age, String address, String phone) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.address = address;
this.phone = phone;
}
Code becomes had to read and maintain with many attributes!
24
17-214
Another solution: Default no-arg constructor plus setters
and getters for every attribute
public class User {
private String firstName;
private String lastName;
private int age;
private String address;
private String phone;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
...
Potentially inconsistent state; mutable
25
17-214
A better solution: Use a Builder to hold build
instructions.
public class User {
private final String firstName;
private final String lastName;
private final int age;
private final String address;
private final String phone;
private User(UserBuilder builder) {
this.firstName = builder.firstName;
this.lastName = builder.lastName;
}
public String getFirstName() { … }
public String getLastName() { … }
}
public static class Builder {
private final String firstName;
private final String lastName;
private int age;
private String address;
private String phone;
private UserBuilder(String firstName,
String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public UserBuilder age(int age) {
this.age = age;
return this;
}
public UserBuilder phone(String phone) {
this.phone = phone;
return this;
}
}
new User.Builder(“Fred”, “Rogers”)
.age(30)
.phone(“1234567”)
.address(...)
.build();
26
17-214
2. Builder
Intent: separate construction of complex object from
representation so same creation process can create
different representations
Use case: converting rich text to various formats
Key types: Builder, ConcreteBuilders, Director, Products
JDK: java.lang.StringBuilder, java.lang.StringBuffer
27
17-214
3. Factory Method
Intent: abstract creational method that lets subclasses
decide which class to instantiate
Use case: creating documents in a framework
Key types: Creator, which contains abstract method to
create an instance
JDK: Iterable.iterator()
28
17-214
Illustration: Factory Method
public interface Iterable<E> {
public Iterator<E> iterator();
}
public class ArrayList<E> implements List<E> {
public Iterator<E> iterator() { ... }
...
}
public class HashSet<E> implements Set<E> {
public Iterator<E> iterator() { ... }
...
}
Collection<String> c = ...;
for (String s : c) // Creates an Iterator appropriate to c
System.out.println(s);
29
17-214
Problem: There should only be one instance of a class,
and that class should be globally accessible.
30
17-214
Prevent client from creating instances by using a private
default constructor
public final class Elvis {
public static final ELVIS = new Elvis();
private Elvis() { }
...
}
31
17-214
Better: Use a static method to create and retrieve the
single instance
public final class Elvis {
private static Elvis ELVIS;
private Elvis() { }
public static Elvis getInstance() {
if (ELVIS == null)
ELVIS = new Elvis();
return ELVIS;
}
...
}
Can you spot the bug in this code?
32
17-214
Better: Use a static method to create and retrieve the
single instance
public final class Elvis {
private static Elvis ELVIS;
private Elvis() { }
public synchronized static Elvis getInstance() {
if (ELVIS == null)
ELVIS = new Elvis();
return ELVIS;
}
...
}
33
17-214
Even better: Use an Enum for guaranteed serialization
and thread safety!
public enum Elvis {
ELVIS;
void sing(Song song) { … }
void eat(Food food) { … }
void playGuitar(Guitar guitar) { … }
...
}
34
17-214
5. Singleton
Intent: ensuring a class has only one instance
Use case: GoF say print queue, file system, company in
an accounting system
Compelling uses are rare but they do exist (e.g., stateless
function objects, EmptySet, etc.)
Key types: Singleton
JDK: java.lang.Runtime.getRuntime(),
java.util.Collections.emptyList()
35
17-214
Aside: Singleton is an instance-controlled class
Static utility class: non-instantiable
difficult to mock; violates information hiding
Enum: one instance per value, all values known at
compile time
Interned class: one canonical instance per value; new
values created at runtime
Used for values -128 to 127 by Integer.valueOf
36
17-214
Generally prefer singletons over static utility classes
If you ever might need more than one instance in the future.
If you need an object that implements other interfaces.
Allows mocking and dependency injection
37
17-214
II. Structural Patterns
1. Adapter
2. Bridge
3. Composite
4. Decorator
5. Façade
6. Flyweight
7. Proxy
38
17-214
1. Adapter
Intent: convert interface of a class into one that
another class requires, allowing interoperability
make things work together after they're designed
Use case: numerous, e.g., arrays vs. collections
Key types: Target, Adaptee, Adapter
JDK: Arrays.asList(T[])
39
17-214
Illustration: Adapter
Have this and this? Use this!
Adaptee Target
Adapter
40
17-214
Problem: You want to treat structures of objects as if
they were an individual object.
Common operations: rotate, translate, explode, ...
41
17-214
Compose objects into tree structures, and implement a
common interface.
House
Wall
Brick
* walls
* bricks
1
1
<<interface>>
PhysicalObject
public interface PhysicalObject {
public void rotate(Orientation orientation);
public void translate(Vector translation);
public void explode(int explosiveness);
...
}
42
17-214
3. Composite
Intent: compose objects into tree structures. Let clients
treat primitives & compositions uniformly.
Use case: GUI toolkit (widgets and containers)
Key type: Component that represents both primitives
and their containers
JDK: javax.swing.JComponent
43
17-214
5. Façade
Intent: provide a simple unified interface to a set of
interfaces in a subsystem
GoF allow for variants where the complex underpinnings are
exposed and hidden
Use case: any complex system; GoF use compiler
Key types: Façade (the simple unified interface)
JDK: java.util.concurrent.Executors
44
17-214
Illustration: Façade
Façade
Subsystem classes
45
17-214
Problem: Imagine implementing a forest of individual
trees in a realtime game
http://gameprogrammingpatterns.com/flyweight.html
46
17-214
Trick: most of the fields in these objects are the same
between all of those instances
http://gameprogrammingpatterns.com/flyweight.html
47
17-214
6. Flyweight
Intent: use sharing to support large numbers
of fine-grained objects efficiently
Use case: characters in a document
Key types: Flyweight (instance-controlled!)
Some state can be extrinsic to reduce number of
instances
JDK: Common! All enums, many others
j.u.c.TimeUnit has number of units as extrinsic
state
48
17-214
7. Proxy
Intent: surrogate for another object
Use case: delay loading of images till needed
Key types: Subject, Proxy, RealSubject
GoF mention several flavors
virtual proxy – stand-in that instantiates lazily
remote proxy – local representative for remote obj
protection proxy – denies some ops to some users
smart reference – does locking or ref. counting, e.g.
JDK: remote method invocation, collections wrappers
49
17-214
Illustration: Proxy
Virtual Proxy
Smart Reference Remote Proxy
SynchronizedList ArrayList
aTextDocument
image
anImage
data
in memory
on disk
anImageProxy
fileName
Client
Proxy
Server
50
17-214
III. Behavioral Patterns
1. Chain of Responsibility
2. Command
3. Interpreter
4. Iterator
5. Mediator
6. Memento
7. Observer
8. State
9. Strategy
10. Template method
11. Visitor
51
17-214
Problem: An event or request should be handled by one
of its ancestral objects
https://refactoring.guru/design-patterns/chain-of-responsibility
52
17-214
1. Chain of Responsibility
Intent: avoid coupling sender to receiver by passing
request along until someone handles it
Use case: context-sensitive help facility
Key types: RequestHandler
JDK: ClassLoader, Properties
Exception handling could be considered a form of Chain
of Responsibility pattern
53
17-214
Problem: Components are tightly coupled.
https://refactoring.guru/design-patterns/mediator
Makes it difficult to understand, test, and reuse code :-(
54
17-214
Solution: Introduce a mediator to allow components to
communicate indirectly
https://refactoring.guru/design-patterns/mediator
55
17-214
Analogy: Can you spot the mediator?
56
17-214
5. Mediator
Intent: define an object that encapsulates how a set of
objects interact, to reduce coupling.
𝓞(n) couplings instead of 𝓞(n
2
)
Use case: dialog box where change in one component
affects behavior of others
Key types: Mediator, Components
JDK: Unclear
57
17-214
Problem: Without violating encapsulation, allow client of
Editor to capture the object’s state and restore later
I.e., add an “undo” ability to a text editor.
58
17-214
public class Editor {
private String text;
private Font font;
private Position cursorPosition;
// a poor solution :-(
public void setState(String text, Font font, ...) {
this.text = text;
...
}
}
Problem: Without violating encapsulation, allow client of
Editor to capture the object’s state and restore later
Why is it a bad idea for a client to use setState to restore state?
59
17-214
public class Editor {
private String text;
private Font font;
private Position cursorPosition;
// a better solution :-)
private class Memento {
private String text;
...
}
public Memento save() {
return new Memento(text, font, ...);
}
public void restore(Memento memento) {
text = memento.text;
...
Problem: Without violating encapsulation, allow client of
Editor to capture the object’s state and restore later
60
17-214
6. Memento
Intent: without violating encapsulation, allow client to
capture an object’s state, and restore later
Use case: when you need to provide an undo
mechanism in your applications, when the internal
state of an object may need to be restored at a later
stage (e.g., text editor)
Key type: Memento (opaque state object)
JDK: none that I’m aware of
61
17-214
Problem: An object should behave differently based upon
its internal state.
https://gameprogrammingpatterns.com/state.html
public class GameCharacter {
public void handleInput(Input input) {
...
}
}
62
17-214
<<interface>>
CharacterState
Solution: Delegate behavior to a State object!
DuckingState
DivingState
StandingState
JumpingState
Character
63
17-214
8. State
Intent: allow an object to alter its behavior when
internal state changes. “Object will appear to change
class.”
Use case: TCP Connection, Game AI
Key type: State (Object delegates to state!)
JDK: none that I’m aware of, but
Works great in Java
Use enums as states
Use AtomicReference<State> to store it
64
17-214
Conclusion
Now you know most of the Gang of Four patterns
Definitions can be vague
Coverage is incomplete
But they’re extremely valuable
They gave us a vocabulary
And a way of thinking about software
Look for patterns as you read and write software
GoF, non-GoF, and undiscovered