House DZ Refcard 008 Design Patterns 2023

Download as pdf or txt
Download as pdf or txt
You are on page 1of 11

008

Design Patterns
CONTENTS

•  Fundamental Concepts
of Object-Oriented Design

Blueprint Solutions to Common Problems


•  Gang of Four Design Patterns

•  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.

Abstraction is the removal of unnecessary details and the focus on


pertinent details for a given context. For example, when we look at a
car, we see doors, body panels, windows, wheels, and the like. If we
focus on the wheels, we may go into a deeper level of abstraction and
see rubber tires, metal rims, and air. Another level deeper, and we
may focus on composites in the rubber compound or the nitrogen and
oxygen particles bouncing within the tire that constitute tire pressure.
The level of abstraction we select will depend on the particular problem
and the problem's context.

REFCARD | AUGUST 2023 1


REFCARD | DESIGN PATTERNS

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

Vehicle vehicleA = new Sedan(); +createProductA()


+createProductB() AbstractProductA
Vehicle vehicleB = new Truck();

vehicleA.drive(); ConcreteFactory1 ConcreteFactory2 ConcreteProductA2 ConcreteProductA1

vehicleB.drive();
AbstractProductB

In this case, when drive() is called on vehicleA, it will use the


ConcreteProductB2 ConcreteProductB1
Sedan, and when drive() is called on vehicleB, it will use the
Truck implementation.
Table 1
CONSIDERATIONS
In addition to OO design principles, there are two additional points we SUMMARY USE CASES

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

−  By introducing delegation, we can also allow other objects


to change the behavior of our object in useful ways since Example: An abstract factory can be used when interacting with
the particular logic executed will vary at runtime. file systems, where one family of objects pertains to Windows, while
•  Inheritance vs. aggregation – Delegated logic can be another family of objects pertains to Linux. When a client requests the
implemented by either inheriting from a base class and objects used to interact with the file system on a Windows system, the
overriding a method, or by introducing an attribute and calling Windows factory is provided; likewise, the Linux factory is provided
that object to do work. when interacting with a Linux system.

−  In both cases, the particular logic executed can vary BUILDER


since the class does not know what subclass is inheriting The builder pattern abstracts the logic for creating an object into a
it, and the object does not know the runtime type of the set of steps.
particular attribute.
Figure 2: Builder pattern
−  In most cases, aggregation is preferred, because it allows
for easier runtime variation of behavior (an attribute can be
Director Builder
injected or replaced at runtime) and reduces the number of
subclasses that must be created to vary behavior. + construct() + buildPartA()
+ buildPartB()
GANG OF FOUR DESIGN PATTERNS + getResult(): Product
The GoF design patterns are divided into three categories: creational,
structural, and behavioral.
Product ConcreteBuilder
CREATIONAL PATTERNS
This type of pattern abstracts the instantiation process of an object. + buildPartA()
+ buildPartB()
ABSTRACT FACTORY + getResult(): Product
The abstract factory pattern creates families or groups of objects
without knowing their implementation.
SEE TABLE 2 ON NEXT PAGE
SEE FIGURE 1 IN NEXT COLUMN

REFCARD | AUGUST 2023 2


REFCARD | DESIGN PATTERNS

Table 2 Figure 4: Prototype pattern

SUMMARY USE CASES Client Prototype

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

Example: The Java Object class includes a clone() method,


Product Creator
which allows a client to clone any object that implements the
+ operation() Cloneable interface. There is nuance to the implementation of
# factoryMethod(): Product
the clone() method that deviates from a strict prototype pattern
implementation, but conceptually, any class that implements
Cloneable can be cloned.

ConcreteProduct ConcreteCreator SINGLETON


# factoryMethod(): Product The singleton pattern ensures that a class has only one object created,
which is accessed through a single entry point.

Table 3 Figure 5: Singleton pattern

SUMMARY USE CASE


Singleton
Defines an algorithm in a superclass based on Delegating the
the creation of an object, which is represented creation logic to - (static) instance: Singleton
by a factory method with an interface return subclasses
value. Concrete classes then override the factory + (static) getInstance(): Singleton
method and return a concrete object.

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.

REFCARD | AUGUST 2023 3


REFCARD | DESIGN PATTERNS

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

STRUCTURAL PATTERNS Avoids a combinatorial explosion •  Avoiding permanent relationship


Structural patterns compose classes and objects to form larger structures. of concrete classes by separating between an abstraction and its
a single class hierarchy into two implementation
ADAPTER or more hierarchies. One of the
•  Reducing the proliferation of
hierarchies (the abstraction) is
The adapter pattern allows an object with one interface to be used implementations as the number of
maintained, and the separated
where another interface is expected. class hierarchies grow
hierarchy (the implementor)
is added as an attribute of the •  Sharing implementations among
Figure 6: Adapter pattern first hierarchy. multiple objects

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

+ request() adaptee.specificRequest() number of possibilities for each hierarchy grows.

Instead, we can create a Sedan implementation for a Vehicle interface


Table 6 that has two properties, Color and Trim, where Red implements the

SUMMARY USE CASES Color interface and Sport implements the Trim interface.

Creates an adapter class with the target •  Reusing an COMPOSITE


interface and embeds the original object existing class, but The composite pattern creates a hierarchy of nested objects, where
(adaptee) as either a subclass or an attribute. its interface does
The adapter then implements its methods the client does not know if it is performing an operation on a single leaf
not match the
using logic from the original object or class. desired interface object or an object representing a collection of sub-objects.
Note: There are class and object adapters; class •  Adding an
adapters use inheritance to achieve adaptation, Figure 8: Composite pattern
interface to an
while object adapters use aggregation. Object existing class is
Client Component
adapters are more common than class adapters impractical children
for many modern applications. + operation()
+ add(Component)
+ remove(Component)

Example: The Java InputStreamReader adapter class contains


constructors that accept an InputStream and implements the Reader
interface. By creating an InputStreamReader using this constructor, Leaf Composite

an InputStream can be adapted to a Reader and used wherever a for c in children:


+ operation()
c.operation()
Reader is required. Likewise, an OutputStream can be adapted to a
Writer using the OutputStreamWriter class.
Table 8

BRIDGE SUMMARY USE CASES


The bridge pattern splits orthogonal concepts in a single class
Creates an interface that defines a •  Needing to create
hierarchy into multiple hierarchies. component, then creates two categories of tree structures of
concrete implementations: terminal nodes arbitrary depth
Figure 7: Bridge pattern called leaves and composites that contain a
•  Wanting clients to treat
collection of sub-objects of that interface
Abstraction impl Implementor an entire structure
Client called children. When a client calls an
uniformly without
+ operation() + operationImpl() interface method, the composite calls the
handling collections of
same method on all its children.
impl.operationImpl()
objects and singular
objects differently
RefinedAbstraction ConcreteImplementorA ConcreteImplementorB
Note: The children of composites can be
both leaves and other composites.
+ operationImpl() + operationImpl()

SEE TABLE 7 IN NEXT COLUMN

REFCARD | AUGUST 2023 4


REFCARD | DESIGN PATTERNS

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

can add clutter to a method definition. + operation(extrinsicState)

Instead of adding logging directly to the method, we can create a


decorator that logs the start and completion of the method call,
allowing us to supplement the logic contained in the wrapped object ConcreteFlyweight UnsharedConcreteFlyweight
without changing the wrapped object itself.
- intrinsicState - allState

FAÇADE + operation(extrinsicState) + operation(extrinsicState)

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

REFCARD | AUGUST 2023 5


REFCARD | DESIGN PATTERNS

Table 11 Example: A cache that reduces the number of calls made to a


database. If we are trying to access books in a database, we can create
SUMMARY USE CASES
a BookRepository interface with a findById(long) method, and
Extracts shared, constant state •  Limiting memory used we can create a MongoDbBookRepository to find Book objects in our
of a component into a class that by an application
MongoDB database. We can then create a CachedBookRepository
represents the intrinsic state. The
•  Reducing the total that caches Book objects in memory and has a reference to the
component then stores this intrinsic
number of objects used
state as an attribute and adds MongoDbBookRepository to find Book objects that are not already
in an application
properties for its state that vary in the cache.
(called extrinsic state). •  Sharing state when
identity is not important
When new instances of the component When a client calls findById(long), if the Book object is already
(conceptually distinct
are created, each instance has its in the cache, it is immediately returned without querying the
objects may be identical
own extrinsic state but shares its
since they are shared) MongoDB database, thus limiting the number of queries to the
intrinsic state.
MongoDB instance.

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.

Figure 12: Proxy pattern

Client Subject ConcreteHandler1 ConcreteHandler2

+ request()
+ handle(Request) + handle(Request)

realSubject Table 13
RealSubject Proxy

... ... SUMMARY USE CASES


...
+ request() + request() + requestSubject.request()
... ... ... Creates an interface for handlers, •  Handling errors when the exact
and each concrete handler stores error is not known beforehand
a reference to the next handler
•  Handling arbitrary requests that
Table 12 (successor) in the chain. Each
may require different levels of
handler either handles the request
SUMMARY USE CASES granularity
or passes it onto the next handler.
Defines an interface for a subject, then creates •  Limiting access
concrete implementations of this interface. to a service
Creates a proxy implementation, where a Example: Although the code is opaque to the application, the Java
•  Altering the
concrete implementation (real subject) is stored exception handling uses the chain of responsibility concept. When an
behavior of an
in the proxy as an attribute. The proxy wraps exception is thrown, the Java Virtual Machine (JVM) looks to see if the
existing service
this implementation and controls access to
•  Caching data current context on the stack can handle the exception. If it can, the
it. Note: Proxies are similar to decorators, but
proxy controls access while decorators provide exception handler — defined using a catch block — is called; otherwise,
additional capability. the stack is popped, and the next context is checked to see if it can
handle the exception. This process continues until a suitable exception
handler is found or the entire stack is popped.

REFCARD | AUGUST 2023 6


REFCARD | DESIGN PATTERNS

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.

SUMMARY USE CASES


ITERATOR
Creates a command interface, which •  Needing to store a The iterator pattern abstracts the traversal of collections without
usually has a single method that takes no command for execution exposing the details of the collection.
parameters. Concrete commands store a at a later time
reference to a receiver object that knows Figure 16: Iterator pattern
•  Needing to execute a
how to execute the command with certain
command by a different
parameters as well as the parameters
component than the Aggregate Client Iterator
for the command. When a client calls the
original handler
command method, the concrete command +createIterator(): Iterator +first()
object calls the receiver method, passing +next()
+hasNext()
the required parameters.
+current()

Example: Undo operations can be implemented through the


command pattern. Each time an action is taken, a command object ConcreteAggregate ConcreteIterator

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

+ interpret(Context) Example: The Java Collection interface includes an iterator()


method that returns a concrete implementation of the Iterator
interface. This method is specified by the Iterable interface that the
Collection interface extends.

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

REFCARD | AUGUST 2023 7


REFCARD | DESIGN PATTERNS

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.

ConcreteMediator ConcreteColleague1 ConcreteColleague2

Example: Similar to the command pattern, a memento can also be


used to undo an operation. When an action is taken, a memento is
Table 17 stored in a caretaker and can be used later to undo the action. To undo
the operation, a reference to both the originator and the memento
SUMMARY USE CASES
is required. The command pattern can be used to create a command
Creates a mediator interface, where •  Decoupling a large number object with a reference to both, thus creating a single, self-contained
each concrete mediator stores the logic of interdependent objects
object that can perform the undo operation.
for how to react to given events. Each
•  Reducing the number of
colleague in the system is provided
dependencies a class has OBSERVER
a reference to a mediator object
and notifies the mediator when an •  Facilitating simple The observer pattern allows clients to subscribe to events that are
event occurs. The mediator reacts to communication between a published when an observed object is changed.
each event, notifying the necessary large number of objects
colleagues rather than having each Figure 19: Observer pattern
colleague store references to all other
colleagues and storing the logic for how Subject
observers
Observer
to react to all possible events.
+ attach(Observer) + update()
+ detach(Observer)
+ notify()
Example: The mediator pattern can be used to create an event or
for o in observers:
message broker, where each component that sends and receives o.update()
ConcreteSubject ConcreteObserver
messages can register with the broker. And as messages are sent to
subject
the broker, the broker passes the message onto one or more receivers - state - state

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.

SEE TABLE 18 IN NEXT COLUMN


Example: The Java Event Listener framework is an application of the
observer pattern, where the listener takes the role of the subscriber,
and the object generating the event assumes the role of the subject.
Note that there are many publish-subscribe (pub/sub) frameworks —
not just in Java, but all languages — but pub/sub is not synonymous
with the observer pattern. In some cases, a message broker is used as
a mediator to completely decouple the publisher from the subscriber.

REFCARD | AUGUST 2023 8


REFCARD | DESIGN PATTERNS

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.

Table 20 Example: The Java Comparator interface represents a strategy

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

of an object without checking a conditional statement, such as


AbstractClass
isLocked(), before each method call. Instead, the object can
delegate its methods to a state object. ...
+ templateMethod()
# primitiveOperation1() primitiveOperation1()
When the lock() method is called on the context object, it can # primitiveOperation2() ...
replace the state object with one that does nothing when the methods primitiveOperation2()
...
are called. When unlock() is called, the state object can be replaced
with the original state object that performs the desired logic.
ConcreteClass
STRATEGY
The strategy pattern encapsulates a set of algorithms as objects with a
# primitiveOperation1()
# primitiveOperation2()
common interface.

Figure 21: Strategy pattern Table 22

strategy SUMMARY USE CASES


Context Strategy
Creates an abstract class •  Separating general steps of an
+ contextInterface() + algorithmInterface() algorithm from their implementation
that defines the general order
and structure of an algorithm •  Reducing duplication by extracting
but keeps the primitive the essence of an algorithm into a
operation methods abstract. superclass and allowing subclasses to
ConcreteStrategyA ConcreteStrategyB A concrete class is created, implement the details
which implements the details
•  Controlling the degree of extension of
of each step, varying the
a subclass (allowing only certain parts
SEE TABLE 21 IN NEXT COLUMN behavior of the algorithm
of an algorithm to be overridden but
defined in the abstract class.
making the general steps final)

Example: The Jakarta EE HttpServlet abstract class implements


default behavior for the doGet, doPost, and other basic HTTP methods

REFCARD | AUGUST 2023 9


REFCARD | DESIGN PATTERNS

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.

+ accept(v: Visitor) + accept(v: Visitor)


MODEL-VIEW-CONTROLLER
The model-view-controller (MVC) pattern designates a controller,
v.visitConcreteElementA(this) v.visitConcreteElementB(this) which is responsible for managing a data model that is then displayed
using a desired view. The model acts as a view-agnostic representation
Table 23 of the data being processed, and the controller is responsible for

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.

REFCARD | AUGUST 2023 10


REFCARD | DESIGN PATTERNS

CONCLUSION •  Patterns of Enterprise Application Architecture by Martin Fowler


Design patterns are an essential part of software development, and
•  Refactoring Guru's Design Patterns
knowledge of the most effective patterns is a prerequisite for becoming
an experienced developer. The GoF patterns are the foundational
patterns that have shaped the way software is written for many decades
and are still relevant today. In the years since these patterns were WRITTEN BY JUSTIN ALBANO,
SOFTWARE ENGINEER, IBM
enumerated, software engineering has grown and numerous patterns
— such as the MVC and reactor patterns — have become ubiquitous. Justin Albano is a Software Engineer at IBM
responsible for building software-storage and
Regardless of the software being developed, it is important to study a backup/recovery solutions for some of the largest
worldwide companies, focusing on Spring-based REST
wide range of patterns and continuously practice them in order to gain API and MongoDB development. When not working or writing, he
the experience necessary to apply the right pattern to effectively solve can be found practicing Brazilian Jiu-Jitsu, playing or watching
hockey, drawing, or reading.
the common problems seen in software development.

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

REFCARD | AUGUST 2023 11

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy