House DZ Refcard 008 Design Patterns 2023
House DZ Refcard 008 Design Patterns 2023
House DZ Refcard 008 Design Patterns 2023
Design Patterns
CONTENTS
• Fundamental Concepts
of Object-Oriented Design
• More Patterns
in Software • Conclusion
JUSTIN ALBANO
SOFTWARE ENGINEER, IBM
Design patterns are solutions to common problems seen in software Interfaces are the highest form of abstraction in OO design. Interfaces
development. They are a foundational concept that both novice and represent what an object can do — through methods — while hiding the
advanced developers must continuously study and practice. In this details of how it does it. For example, the interface for a car, Car, may
Refcard, we will dive into the concepts that underpin design patterns be limited to a start() and driveTo(Location) method, neither of
and look at the 23 Gang of Four (GoF) patterns that brought about the which provide any details about how a particular car is started or can
proliferation of design patterns. be driven to a particular location.
We will then look at some common patterns that have evolved over the Implementations of this interface (called concrete classes) are then
years since the GoF patterns were published in 1994, as well as some created, which provide these details, but clients interacting with the
resources that will help us understand the GoF patterns — and design Car interface are unconcerned with these details. This encapsulates
patterns in general — more intuitively. the details and allows clients to focus on the inputs and outputs of the
methods rather than the particular details of how they are performed.
FUNDAMENTAL CONCEPTS
OF OBJECT-ORIENTED DESIGN Polymorphism is the ability of an object to be treated as multiple
Before we dive into the GoF patterns, we must first understand types at the same time. There is static polymorphism, which uses
the fundamental concepts of object-oriented (OO) design and look method overloading, and dynamic polymorphism, which uses method
at two important considerations that provide a groundwork for overriding. Most design patterns focus on dynamic polymorphism,
all OO software patterns. Understanding the GoF design patterns
requires a foundational understanding of OO design. Three of the
fundamental concepts shared by all OO design patterns — and the
GoF design patterns, in particular — are abstraction, interfaces,
and polymorphism.
where a client calls the methods of an interface while unaware of the Figure 1: Abstract factory pattern
actual implementation being used. For example:
AbstractFactory Client
vehicleB.drive();
AbstractProductB
must consider before we can understand different design patterns: Abstracts the creation logic • Creating groups or families
delegation and inheritance vs. aggregation. for a family of objects into an of objects while hiding their
abstract factory interface with instantiation details
• Delegation – In some cases, it is better for a class to make a methods whose return values
• Constraining families of related
decision for itself, such as what type of object to instantiate are also interfaces. Concrete
objects to be used in conjunction
and return from a method or how to perform a given task. In factories then implement the
with one another
factory interface and return
other cases, it may be better for an object to delegate that • Exposing a group of objects, but
concrete objects from the
responsibility to another object. factory methods. only by their interfaces
Defines a builder interface that • Creating a complex object + operation() + clone(): Prototype
contains methods that assemble requiring multiple steps
an object step by step and return
• Creating different concrete
a completely assembled object.
objects depending on the
The returned object can be
creation parameters ConcretePrototype1 ConcretePrototype2
abstracted behind an interface,
allowing different builders to • Creating identical objects by using
the same configured builder + clone(): Prototype + clone(): Prototype
return different concrete objects.
Table 4
Example: The Apache HttpClient library provides the URIBuilder
class, which allows clients to instantiate a builder object, configure the SUMMARY USE CASES
URI components (e.g., protocol, host, port, and query parameters), and Creates a clone method in • Deciding which classes to instantiate
then generate a URI object using the build() method. the interface for a class that at runtime
returns an object of the same
• Reducing the number of class
FACTORY METHOD interface. Concrete classes
hierarchies and factory classes
The factory method abstracts the creation logic for an object in the then implement the interface
and return a copy of the same • Knowing a priori that there are only a
superclass, allowing subclasses to define the concrete object that will few possible states for an object, and
object with the same state
be created. from the clone method. cloning existing objects may be easier
than executing complex creation logic
Figure 3: Factory method pattern
Table 5
Example: Factory methods can be used when creating task objects,
SUMMARY USE CASES
where a Scheduler used to schedule and execute the logic of the
task can be abstracted into a factory method. Subclasses can then Creates a single object (singleton) • Requiring only a single object be
of a class and defines a static created across an application
be created, such as SingleThreadedTask, where the factory
method that returns this singular
• Requiring a single entry point to
method returns a SingleThreadedScheduler that is used to object. Constructors are hidden
an object
execute the task logic. so that clients cannot create
additional objects. • Managing global data
PROTOTYPE
The prototype pattern clones an existing object without requiring a Example: The Spring Inversion of Control (IoC) container allows clients
client to know the concrete type. to create singleton beans. If a bean is declared as a singleton, then only
one instance of the bean will be created per IoC container, and the same
SEE FIGURE 4 IN NEXT COLUMN
object will be used when auto-wiring the bean into dependent objects.
Note that Spring does not implement this bean creation using a static Table 7
method, but it conceptually enforces the singleton pattern.
SUMMARY USE CASES
Target Adaptee
Client
+ request() + specificRequest() Example: There may be multiple properties that define a vehicle,
adaptee
such as its type (sedan, truck, etc.), its color, and its trim (sport,
premium, etc.). Creating a new class for each of these hierarchies, such
Adapter as RedSportSedan, would introduce a combinatorial explosion as the
SUMMARY USE CASES Color interface and Sport implements the Trim interface.
Example: If we want to move files on a file system, we can create a Figure 10: Façade pattern
File interface with a moveTo(Path) method, and have two concrete
Façade
implementations: a composite called Directory and a leaf called Complex System
TextFile. When a client calls the moveTo(Path) method on an
Component Component
arbitrary File object, a TextFile object will move itself, while a
Directory object will move itself as well as all its children (either Component
TextFile or other Directory objects).
DECORATOR
The decorator pattern wraps an existing object to add additional Table 10
responsibility.
SUMMARY USE CASES
Figure 9: Decorator pattern Limits the interface of a complex • Providing a simple interface to a
set of interfaces and classes complex system
Client Component to the client's most important
• Decoupling complex dependencies
+ operation() features. Provides a single point
in a subsystem
(façade) for the client to interact
with the sub-components. • Separating subsystems by
Removes the need to directly introducing façades for each
ConcreteComponent Decorator interact with numerous sub- subsystem and communicating
+ operation() component.operation() components. between the subsystems only
through their façades
ConcreteDecoratorA ConcreteDecoratorB
Example: The Spring IoC container allows clients to programmatically
- addedState + operation() decorator.operation()
+ addedBehavior() addedBehavior() create beans through the ApplicationContext class.
+ operation()
While the Spring IoC container is a complex and intricate system, the
Table 9 ApplicationContext class provides a single entry point that allows
clients to focus on bean creation and retrieval, rather than the details
SUMMARY USE CASES
of the IoC container.
Defines an interface for a component, • Adding responsibility to
then defines decorators that implement classes without changing FLY WEIGHT
that interface, while also wrapping the original class
The flyweight pattern reuses shared components rather than
another implementor as an attribute.
• Changing responsibility instantiating new objects each time a component is needed.
When a method is called on the dynamically by applying or
decorator, the decorator performs some forgoing a decorator
Figure 11: Flyweight pattern
action in addition to calling the same
• Dividing logic into small,
method on the wrapped object.
nested objects FlyweightFactory
Client
+ getFlyweight(key) flyweights
Example: In order to debug issues and track the operation of an
application, it is important to log critical events, but logging statements Flyweight
The façade pattern defines a unified interface for a more complex set
of sub-interfaces and classes.
SEE TABLE 11 ON NEXT PAGE
SEE FIGURE 10 IN NEXT COLUMN
Example: The boxed types in Java each contain a valueOf method BEHAVIORAL PATTERNS
that creates a boxed object from its primitive counterpart — for This category assigns responsibility and defines communication
example, Integer.valueOf(1). The Integer#valueOf(int) among groups of objects.
method will always return the same Integer object for values
CHAIN OF RESPONSIBILITY
between -128 and 127, while other values may also be cached
The chain of responsibility pattern passes a request to a chain of
(e.g., Integer.valueOf(1) == Integer.valueOf(1) will always be
handlers, where each handler is responsible for handling the request or
true, while Integer.valueOf(1000) == Integer.valueOf(1000)
passing it to the next handler.
is not guaranteed to be true).
Figure 13: Chain of responsibility pattern
Note: The implementation used by Interger#valueOf(int) may
not exactly match the flyweight pattern, but it conceptually successor
implements the pattern.
Client Handler
PROXY
The proxy pattern wraps an existing object in order to control access + handle(Request)
to the wrapped object.
+ request()
+ handle(Request) + handle(Request)
realSubject Table 13
RealSubject Proxy
COMMAND Table 15
The command pattern stores a request, along with all the information SUMMARY USE CASE
necessary to handle the request, in a single object that can be executed
Creates an Abstract Syntax Tree (AST) of objects Parsing well-
by any client or queued for execution at a later time. that all implement an expression interface, which defined expressions
contains methods to interpret expressions based of a language
Figure 14: Command pattern on the AST. This pattern is not as commonly used
as the others anymore, and it is usually seen in
Client Invoker Command
more niche applications (e.g., parsers, compilers,
+ execute() regular expression interpreters).
receiver Example: Many times, interpreters are used for resolving trees of
Receiver ConcreteCommand
expressions — mathematical expressions, regular expressions, simple
+ action() - state
natural language expressions, Boolean expressions, and programming
+ execute() receiver.action()
language expressions — into an interpreted result. These sets of
expressions are represented as ASTs and then iteratively interpreted
Table 14 until a result is resolved.
can be created — with the logic and parameters necessary to undo the +createIterator(): Iterator
action — and pushed onto a stack. If the user presses the undo button,
the command objects can be popped from the stack and executed, thus
Table 16
undoing the previous action.
SUMMARY USE CASES
INTERPRETER
Creates an iterator interface that • Abstracting the iteration process
The interpreter creates an object-based representation of a grammar
allows clients to find the next for a collection of objects
and interprets that representation. element in an aggregate and
• Traversing the same collection
check if there is a next element.
using different logic depending
Figure 15: Interpreter pattern Each collection returns one or
on the chosen iterator
more concrete iterators that
traverses the collection in the • Supporting uniform traversal of
Context
desired manner (depth-first, different underlying collections
sequentially, etc.). (e.g., lists, stacks, and queues)
Client AbstractExpression
TerminalExpression NonterminalExpression In Java, any class that implements the Iterable interface can be used
+ interpret(Context) + interpret(Context) in a for-each loop (enhanced for loop), which is made possible by the
iterator returned by each implementing class (see the Java Language
Specification, JLS, 14.14.2).
SEE TABLE 15 IN NEXT COLUMN
MEDIATOR Table 18
The mediator pattern encapsulates the communication between
SUMMARY USE CASES
numerous objects into a single object.
An originator creates a memento object • Snapshotting the state
Figure 17: Mediator pattern that contains the state of the originator at of an originator
the time the memento was instantiated.
• Restoring the state of
mediator The memento is then passed to a
Mediator Colleague an object by a caretaker
caretaker object that stores the memento
from a previously
and can use it to restore the state of the
taken snapshot
originator at a later time.
based on some attribute (e.g., the to field) of the message. + getState() + update()
+ setState()
state = subject.getState()
MEMENTO
The memento pattern stores a snapshot of the state of an object that
Table 19
can be used to restore that state at a later time.
SUMMARY USE CASES
Figure 18: Memento pattern
Creates a subject interface, which stores a • Notifying objects
memento
Originator Memento Caretaker collection of registered concrete subscribers of asynchronous
that implement the subscriber interface. When changes
- state - state
an event occurs, the subject subsequently
• Notifying objects of
+ setMemento(m: Memento) + getState() notifies all the registered subscribers that
changes in arbitrary
+ createMemento(): Memento + setState() the event occurs. This mechanism allows for
publishers
asynchronous event notifications without
a subject or a subscriber knowing the
return new Memento(state) state = m.getState()
implementation details of the other.
STATE Table 21
The state pattern alters the behavior of an object when its internal
SUMMARY USE CASES
state changes.
Creates a context class, which • Abstracting the algorithm an object
Figure 20: State pattern has a reference to a strategy executes at runtime
object that implements
• Sharing algorithmic implementation
Context state State a strategy interface. The
between different objects
method in the context class
+ request() + handle() varies its behavior based on • Separating algorithms from
the results of the algorithm their results
method, allowing the behavior • Reducing the number of conditional
state.handle()
of the context class to vary statements that depend on
based on the specific strategy algorithm implementations
ConcreteStateA ConcreteStateB
object used.
SUMMARY USE CASES interface, where a Comparator object can be passed to a method,
such as Collections#sort(List, Comparator), to compare one
Creates a context class and a state • Changing the state of an
object to another. In the case of Collections#sort, the order that
interface, where the context obtained object at runtime
contains a reference to a state object. The a List is sorted depends on the Comparator strategy that is passed
• Reducing the number of
logic in the context class methods change
conditional statements
to the method.
based on the state, and the context class
that depend on an
provides a method for a client to change TEMPLATE METHOD
object's state
its state. If the state of the context object The template method pattern provides a structure for an algorithm,
changes, the behavior of its methods also
while allowing subclasses to define the specific implementation for
changes accordingly.
each step of the algorithm.
Example: The state pattern can be used to lock the internal state Figure 22: Template method pattern
by returning an HTTP 405 Method Not Allowed response. Clients are PUBLISH-SUBSCRIBE
expected to override at least one of these methods to change how the In many distributed and cloud systems, events and messages flow from
servlet handles a request. one microservice to another without each microservice knowing who
triggered the event or who is listening for its own events. This is possible
VISITOR
because these services implement the pub/sub pattern, where publishers
The visitor pattern separates an algorithm from the objects on which
generate messages without knowing who is listening for them, and
the algorithm is applied.
subscribers listen for messages without knowing who is generating them.
Figure 23: Visitor pattern
The pub/sub pattern is different from the observer pattern because,
Client Visitor in many cases, the pub/sub pattern implementations use a message
+ visitConcreteElementA(ConcreteElementA) or event broker (e.g., RabbitMQ, Java Messaging Service [JMS]) to
+ visitConcreteElementB(ConcreteElementB)
handle the forwarding of events. In this case, subscribers do not
register directly with a publisher, but instead, both the publishers and
subscribers register with a broker that acts like a mediator.
ConcreteVisitor1 ConcreteVisitor2
FILTER
As data flows from a data source to a data sink, it is commonly filtered
ObjectStructure Element
out. To filter out data, a chain of filters is created, where data that
+ accept(v: Visitor) flows out of one filter flows into the next. The Jakarta EE Filter and
FilterChain classes implement the filter pattern, where filters are
applied to incoming servlet requests. Likewise, the Spring Security
ConcreteElementA ConcreteElementB Filter Chain is an implementation of the filter pattern.
SUMMARY USE CASES processing the data and designating the view or views that should
be used to encode or display the data to the user. The MVC pattern is
Creates a visitor algorithm, which has • Performing operations
common among many web frameworks, including Spring Web MVC.
a visit method for each concrete type on a large number of
that implements the element interface. objects based on the
THREAD POOL
The element interface then defines implementation type of
an accept method that consumes a those objects In many concurrent applications, threads are created and then released
visitor object and passes itself as the based on the workload being serviced at any given time. There is non-
• Performing unrelated
argument to the visit method. This trivial overhead in creating new threads, which can cause a significant
operations on an object
pattern uses the concept of double-
structure degradation in performance if workloads fluctuate.
dispatch, where the runtime behavior
of the call depends on both the • Parsing the same object
concrete visitor object selected and the structure in different ways Instead of creating a new thread object each time a thread is needed,
concrete element that it is visiting. a pool can be created where released threads are returned back to
the pool, and subsequent requests for threads can be serviced with
Example: The Java FileVisitor class is an interface that clients threads already created in the pool. The Java ThreadPoolExecutor is
can implement to perform a desired action each time a file is an implementation of the thread pool pattern.
visited. The FileVisitor class can be used in conjunction with the
REACTOR
Files#walkFileTree to iterate through the files under a desired path
The reactor pattern defines a handler service that accepts multiple
and perform the action defined in a concrete FileVisitor object
concurrent incoming requests and dispatches them to request
when each file is visited.
handlers, which each handle a single request. This pattern is common
MORE PATTERNS for handling a large number of concurrent requests because the
While the 23 GoF patterns still stand as the cornerstone of software request handlers are usually small and agile enough to work in parallel
development design patterns, many patterns have been formalized to numerous service requests. The Spring Reactive framework is an
since 1994. This section describes a few of these patterns commonly implementation of the reactor pattern.
seen in our age of web applications and microservices.
FURTHER READING
Understanding and applying design patterns requires diligent practice. 3343 Perimeter Hill Dr, Suite 100
Nashville, TN 37211
The following resources contain excellent descriptions and examples 888.678.0399 | 919.678.0300
of design patterns that can help even the most seasoned developer At DZone, we foster a collaborative environment that empowers developers and
internalize design patterns and instill the experience necessary to tech professionals to share knowledge, build skills, and solve problems through
content, code, and community. We thoughtfully — and with intention — challenge
apply patterns at the right time and place: the status quo and value diverse perspectives so that, as one, we can inspire
positive change through technology.
• Design Patterns: Elements of Reusable Object-Oriented Software by
Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides Copyright © 2023 DZone. All rights reserved. No part of this publication may be
reproduced, stored in a retrieval system, or transmitted, in any form or by means
• Head First Design Patterns: Building Extensible and Maintainable of electronic, mechanical, photocopying, or otherwise, without prior written
permission of the publisher.
Object-Oriented Software by Eric Freeman and Elisabeth Robson