0% found this document useful (0 votes)
69 views103 pages

C++ Design Patterns - A Comprehensive Guide

This document provides a comprehensive guide to C++ design patterns, emphasizing the significance of the 'Gang of Four' design patterns published in 1994. It categorizes the 23 patterns into Creational, Structural, and Behavioral types, and introduces the SOLID principles as foundational concepts for effective object-oriented design. The guide also details the Singleton pattern, illustrating its implementation and challenges in multithreaded environments.

Uploaded by

revanth
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
69 views103 pages

C++ Design Patterns - A Comprehensive Guide

This document provides a comprehensive guide to C++ design patterns, emphasizing the significance of the 'Gang of Four' design patterns published in 1994. It categorizes the 23 patterns into Creational, Structural, and Behavioral types, and introduces the SOLID principles as foundational concepts for effective object-oriented design. The guide also details the Singleton pattern, illustrating its implementation and challenges in multithreaded environments.

Uploaded by

revanth
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 103

A Comprehensive Guide to C++ Design

Patterns

Introduction: The Philosophy and Power of Design


Patterns

The Genesis of a Revolution

In 1994, the landscape of software development was fundamentally altered by the publication
of a single book: Design Patterns: Elements of Reusable Object-Oriented Software.1 Authored
by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides—a group that would
become famously known as the "Gang of Four" (GoF)—this work quickly became a
cornerstone of object-oriented design.3 It captured a wealth of experience by presenting a
catalog of 23 simple, elegant, and reusable solutions to commonly occurring problems in
software design.1 Even decades after its initial release, the book remains an Amazon
bestseller, a testament to its enduring impact on how developers structure, maintain, and
reason about their code.4 The patterns, originally demonstrated with examples in C++ and
Smalltalk, provided a systematic approach to creating flexible and robust software
architectures.1

Beyond Code: Patterns as a Shared Vocabulary

A common misconception among beginners is that a design pattern is a rigid piece of code to
be copied directly into a project.5 In reality, a design pattern is more akin to a blueprint or a
template; it is a description of a proven solution to a recurring problem that can be adapted to
many different situations.5 The true power of design patterns lies not just in the solutions they
offer, but in the shared language they create among developers.7

When a developer mentions using a "Factory" to create objects or an "Observer" to handle


notifications, other developers familiar with these patterns immediately understand the
underlying architecture, the relationships between the components, and the problem being
solved. This common vocabulary streamlines communication, improves collaboration, and
serves as a powerful form of documentation embedded directly in the code's naming
conventions (e.g., ShapeFactory, OrderCommand).9 Mastering design patterns is therefore not
just about learning to implement solutions, but about learning to speak a universal language
of software design.

The Three Pillars: Categorizing the Patterns

The 23 GoF patterns are organized into three fundamental categories based on their purpose:
Creational, Structural, and Behavioral.4 This classification provides a framework for
understanding the distinct problem domains each pattern addresses.
●​ Creational Patterns: These patterns are concerned with the process of object creation.
Their primary goal is to abstract the instantiation process, making a system independent
of how its objects are created, composed, and represented. This increases flexibility by
decoupling the client from the specific classes it needs to instantiate.3
●​ Structural Patterns: These patterns describe how classes and objects can be composed
to form larger, more complex structures. They focus on simplifying the system's structure
by identifying relationships between entities, often using inheritance and composition to
create new functionalities.3
●​ Behavioral Patterns: These patterns are concerned with algorithms and the assignment
of responsibilities among objects. They describe how objects interact and communicate,
promoting loose coupling and flexibility in how these interactions are carried out.4

Chapter 1: The Bedrock of Design - SOLID Principles in


C++

Before delving into the 23 GoF patterns, it is essential to understand the foundational
principles that underpin robust object-oriented design. The SOLID principles, an acronym for
five key design guidelines, are not patterns themselves but are abstract concepts that
motivate the structure of many patterns. A firm grasp of SOLID is a prerequisite for
appreciating why the GoF patterns are designed the way they are and for applying them
effectively.15

The Single Responsibility Principle (SRP)

●​ Concept: The SRP states that "a class should have only one reason to change".11 This
means a class should have a single, well-defined responsibility. When a class has multiple
responsibilities, changes to one can inadvertently affect the others, leading to fragile
code. Adhering to SRP results in classes with high cohesion and greater robustness.17
●​ C++ Example: Consider a Journal class that manages a collection of entries but also
handles saving the journal to a file.​
C++​
// Violation of SRP​
struct Journal {​
std::string title;​
std::vector<std::string> entries;​

void add_entry(const std::string& entry);​

// Persistence is a separate responsibility​
void save(const std::string& filename) {​
std::ofstream ofs(filename);​
for (auto& e : entries) {​
ofs << e << std::endl;​
}​
}​
};​

This class has two reasons to change: a change in how entries are managed, or a change
in how persistence is handled. To conform to SRP, these responsibilities should be
separated.​
C++​
// Conforming to SRP​
struct Journal {​
std::string title;​
std::vector<std::string> entries;​
void add_entry(const std::string& entry);​
};​

struct PersistenceManager {​
static void save(const Journal& j, const std::string& filename) {​
std::ofstream ofs(filename);​
for (auto& e : j.entries) {​
ofs << e << std::endl;​
}​
}​
};​

The Open/Closed Principle (OCP)

●​ Concept: The OCP states that "software entities... should be open for extension, but
closed for modification".11 This means you should be able to add new functionality to a
system without altering existing, tested code. This is typically achieved through
polymorphism and abstract interfaces.17
●​ C++ Example: Imagine a function that filters a list of products based on a specific
attribute, like color.​
C++​
// Violation of OCP​
enum class Color { Red, Green, Blue };​
struct Product { std::string name; Color color; };​

struct ProductFilter {​
std::vector<Product*> by_color(std::vector<Product*> items, Color color) {​
std::vector<Product*> result;​
for (auto& i : items) {​
if (i->color == color) {​
result.push_back(i);​
}​
}​
return result;​
}​
};​

If we need to add a filter for size, we would have to modify the ProductFilter class. A
better approach uses abstraction.​
C++​
// Conforming to OCP​
template <typename T> struct Specification {​
virtual bool is_satisfied(T* item) const = 0;​
};​

struct ColorSpecification : Specification<Product> {​
Color color;​
explicit ColorSpecification(const Color color) : color{color} {}​
bool is_satisfied(Product* item) const override {​
return item->color == color;​
}​
};​

template <typename T> struct Filter {​
virtual std::vector<T*> filter(std::vector<T*> items, const Specification<T>& spec) = 0;​
};​

struct BetterFilter : Filter<Product> {​
std::vector<Product*> filter(std::vector<Product*> items, const Specification<Product>& spec)
override {​
std::vector<Product*> result;​
for (auto& item : items) {​
if (spec.is_satisfied(item)) {​
result.push_back(item);​
}​
}​
return result;​
}​
};​

Now, new filter criteria can be added by creating new Specification classes without
modifying BetterFilter.

The Liskov Substitution Principle (LSP)

●​ Concept: The LSP states that "functions that use pointers or references to base classes
must be able to use objects of derived classes without knowing it".19 In essence, a
subclass must be substitutable for its parent class without altering the correctness of the
program.11 This principle is about maintaining behavioral contracts.
●​ C++ Example: The classic example involves a Rectangle base class and a Square derived
class.​
C++​
class Rectangle {​
protected:​
int width, height;​
public:​
Rectangle(int w, int h) : width{w}, height{h} {}​
virtual void set_width(int w) { width = w; }​
virtual void set_height(int h) { height = h; }​
int get_area() const { return width * height; }​
};​

// Violation of LSP​
class Square : public Rectangle {​
public:​
Square(int size) : Rectangle{size, size} {}​
void set_width(int w) override { this->width = this->height = w; }​
void set_height(int h) override { this->width = this->height = h; }​
};​

void process(Rectangle& r) {​
int w = 10;​
r.set_width(w);​
// For a Rectangle, we expect height to be unchanged.​
// For a Square, set_width also changes the height, breaking the expectation.​
}​

A function process that works with a Rectangle& would produce unexpected behavior if a
Square is passed in, because the Square's setters modify both dimensions, violating the
implicit behavior of the Rectangle's setters. This indicates a flawed class hierarchy; a
square is not behaviorally a rectangle in this context.

The Interface Segregation Principle (ISP)

●​ Concept: The ISP states that "clients should not be forced to depend upon interfaces
that they do not use".11 This principle advocates for creating smaller, more specific
interfaces rather than large, monolithic ones.
●​ C++ Example: Consider a single interface for a multi-function machine.​
C++​
// Violation of ISP​
struct IMachine {​
virtual void print(const std::string& doc) = 0;​
virtual void scan(const std::string& doc) = 0;​
virtual void fax(const std::string& doc) = 0;​
};​

struct SimplePrinter : IMachine {​
void print(const std::string& doc) override { /*... */ }​
void scan(const std::string& doc) override { /* empty, not applicable */ }​
void fax(const std::string& doc) override { /* empty, not applicable */ }​
};​

The SimplePrinter is forced to implement scan and fax, methods it does not need. The
solution is to segregate the interface.​
C++​
// Conforming to ISP​
struct IPrinter {​
virtual void print(const std::string& doc) = 0;​
};​
struct IScanner {​
virtual void scan(const std::string& doc) = 0;​
};​

struct Printer : IPrinter {​
void print(const std::string& doc) override { /*... */ }​
};​

struct Photocopier : IPrinter, IScanner {​
void print(const std::string& doc) override { /*... */ }​
void scan(const std::string& doc) override { /*... */ }​
};​

The Dependency Inversion Principle (DIP)

●​ Concept: The DIP has two parts: 1) High-level modules should not depend on low-level
modules; both should depend on abstractions. 2) Abstractions should not depend on
details; details should depend on abstractions.11 This is the core principle of decoupling.
●​ C++ Example: A high-level Reporting module directly depends on a low-level
DatabaseLogger.​
C++​
// Violation of DIP​
class DatabaseLogger {​
public:​
void log(const std::string& message) { /*... writes to DB... */ }​
};​

class Reporting {​
DatabaseLogger logger; // Direct dependency on a concrete low-level module​
public:​
void generate_report() {​
logger.log("Generating report...");​
}​
};​

This couples Reporting to the DatabaseLogger. If we want to switch to a file logger, we
must modify the Reporting class. The solution is to introduce an abstraction.​
C++​
// Conforming to DIP​
struct ILogger {​
virtual ~ILogger() = default;​
virtual void log(const std::string& message) = 0;​
};​

class DbLogger : public ILogger {​
public:​
void log(const std::string& message) override { /*... writes to DB... */ }​
};​

class Reporting {​
ILogger& logger; // Depends on an abstraction​
public:​
Reporting(ILogger& logger) : logger{logger} {}​
void generate_report() {​
logger.log("Generating report...");​
}​
};​

Now, the Reporting module depends on the ILogger interface, and any class that
implements this interface can be provided at runtime, effectively inverting the
dependency.

Part I: Creational Design Patterns


Creational patterns abstract the object-instantiation process, decoupling a system from its
concrete classes.3 They provide flexibility in

what gets created, who creates it, and how it gets created. Before a detailed examination of
each pattern, the following table provides a high-level overview to help differentiate their core
intents.

Pattern Intent Common C++ Use Case

Singleton Ensure a class has only one Logging service,


instance and provide a configuration manager,
global point of access. database connection pool.

Factory Method Define an interface for A framework that needs to


creating an object, but let create application-specific
subclasses decide which objects (e.g., Document in
class to instantiate. a word processor vs. Sheet
in a spreadsheet app).

Abstract Factory Provide an interface for Creating UI widget toolkits


creating families of related for different operating
objects without specifying systems (e.g.,
their concrete classes. WindowsButton and
WindowsScrollBar vs.
MacButton and
MacScrollBar).

Builder Separate the construction Building a complex query


of a complex object from object for a database, or
its representation. configuring a Pizza object
with multiple optional
toppings.

Prototype Specify the kinds of objects Creating game objects


to create using a (e.g., enemies) where a
prototypical instance, and base template is
create new objects by configured and then cloned
copying this prototype. repeatedly.
Chapter 2: Singleton

Intent

The Singleton pattern's intent is to ensure that a class has only one instance and to provide a
single, global point of access to that instance.6

Problem

Certain components in a system are inherently unique, such as a configuration manager, a


logging service, or a hardware interface. Allowing multiple instances of these components
could lead to inconsistent states, resource contention, or incorrect behavior.21 The Singleton
pattern solves this by centralizing control over instantiation, guaranteeing that only one object
of its kind exists throughout the application's lifecycle.

Structure and Participants

The implementation of a Singleton class involves several key structural elements to enforce its
uniqueness 22:
●​ Private Constructor: Prevents direct instantiation of the class from outside code.
●​ Deleted Copy Constructor and Assignment Operator: Prevents clients from creating
copies of the single instance.
●​ Static Instance Member: A private static member to hold the one and only instance of
the class.
●​ Public Static Accessor Method: A public static method (commonly named
getInstance()) that provides the global access point. This method is responsible for
creating the instance on its first call and returning that same instance on all subsequent
calls.
C++ Implementation Deep Dive

Naive Singleton (Not Thread-Safe)

A basic implementation illustrates the core logic but is unsafe in multithreaded environments.

C++

#include <iostream>​

class Singleton {​
private:​
static Singleton* instance_;​
Singleton() {​
std::cout << "Singleton instance created.\n";​
}​

public:​
Singleton(const Singleton&) = delete;​
Singleton& operator=(const Singleton&) = delete;​

static Singleton* getInstance() {​
if (instance_ == nullptr) {​
instance_ = new Singleton();​
}​
return instance_;​
}​
};​

Singleton* Singleton::instance_ = nullptr;​

The primary issue here is the race condition within getInstance(). If two threads
simultaneously evaluate instance_ == nullptr as true, both will proceed to create a new
instance, violating the pattern's core guarantee.24

Thread-Safe Singleton (Modern C++ - Meyers' Singleton)

Since C++11, the language provides a simple, elegant, and thread-safe way to implement the
Singleton pattern using a static local variable. This is often called the "Meyers' Singleton."

C++

#include <iostream>​

class Singleton {​
private:​
Singleton() {​
std::cout << "Singleton instance created.\n";​
}​

public:​
Singleton(const Singleton&) = delete;​
Singleton& operator=(const Singleton&) = delete;​

static Singleton& getInstance() {​
static Singleton instance; // Initialization is thread-safe since C++11​
return instance;​
}​

void someBusinessLogic() {​
std::cout << "Executing business logic.\n";​
}​
};​

int main() {​
Singleton& s1 = Singleton::getInstance();​
Singleton& s2 = Singleton::getInstance();​
s1.someBusinessLogic();​
// s1 and s2 refer to the same object​
return 0;​
}​

The C++ standard guarantees that the static Singleton instance; will be initialized only once,
even when getInstance() is called concurrently from multiple threads. This approach is the
preferred method in modern C++ due to its simplicity and guaranteed thread safety without
manual locking.25

Thread-Safe Singleton (Double-Checked Locking)

Before C++11, a common technique was "double-checked locking" using mutexes. While it is
more complex and now largely unnecessary, it is a useful concept to understand.

C++

#include <iostream>​
#include <mutex>​
#include <atomic>​

class Singleton {​
private:​
static std::atomic<Singleton*> instance_;​
static std::mutex mutex_;​

Singleton() {​
std::cout << "Singleton instance created.\n";​
}​

public:​
Singleton(const Singleton&) = delete;​
Singleton& operator=(const Singleton&) = delete;​

static Singleton* getInstance() {​
Singleton* p = instance_.load(std::memory_order_acquire);​
if (p == nullptr) { // First check (without lock)​
std::lock_guard<std::mutex> lock(mutex_);​
p = instance_.load(std::memory_order_relaxed);​
if (p == nullptr) { // Second check (with lock)​
p = new Singleton();​
instance_.store(p, std::memory_order_release);​
}​
}​
return p;​
}​
};​

std::atomic<Singleton*> Singleton::instance_{nullptr};​
std::mutex Singleton::mutex_;​

This version uses an atomic pointer and a mutex to ensure thread safety. The first check
avoids the expensive lock acquisition if the instance already exists. The second check inside
the lock prevents a race condition if multiple threads pass the first check simultaneously.21

Consequences

●​ Pros:
○​ Guaranteed Single Instance: Ensures only one object of a class is ever created.22
○​ Global Access: Provides a convenient, globally accessible point to the instance.22
○​ Lazy Initialization: The instance is created only when it is first requested.22
●​ Cons:
○​ Global State: The pattern introduces global state into an application, which can
make code harder to reason about and debug. It creates hidden dependencies
between components.24
○​ Violation of SRP: The Singleton class becomes responsible for both its core logic
and managing its own lifecycle.
○​ Testing Difficulties: Tightly couples components to the concrete Singleton class,
making it difficult to substitute mock objects during unit testing.24

The prevalence of these drawbacks has led many experienced developers to consider the
Singleton an "anti-pattern".24 The need for a Singleton can often indicate a deeper design
issue, such as improper dependency management. In many cases, explicitly passing a shared
object via Dependency Injection leads to a more modular, testable, and maintainable design.

Chapter 3: Factory Method


Intent

The Factory Method pattern defines an interface for creating an object but lets subclasses
decide which class to instantiate. It allows a class to defer the instantiation process to its
subclasses.6

Problem

Consider a generic framework for building applications, such as a document editor. The
framework might define an abstract Application class that knows how to create, open, and
save documents. It works with an abstract Document class. However, the framework itself
does not know what kind of concrete documents will be created—that is up to the specific
application built using the framework. For example, a text editor application will create
TextDocument objects, while a spreadsheet application will create Spreadsheet objects. The
framework's Application class should not be hardcoded to create TextDocuments, as this
would make it unusable for the spreadsheet application.

Structure and Participants

The Factory Method pattern elegantly solves this by delegating the creation decision to
subclasses.27
●​ Product: (Document) Defines the interface for objects the factory method creates.
●​ ConcreteProduct: (TextDocument, Spreadsheet) Implements the Product interface.
●​ Creator: (Application) Declares the factory method, which returns an object of type
Product. It may also define a default implementation. The Creator's primary role is often
its core business logic, which relies on the Product objects created by the factory
method.
●​ ConcreteCreator: (TextEditorApplication, SpreadsheetApplication) Overrides the factory
method to return an instance of a specific ConcreteProduct.

This structure is sometimes referred to as a "Virtual Constructor" because it provides a way to


create objects polymorphically, something C++ constructors cannot do directly.29
C++ Implementation Deep Dive

The following example implements the document editor framework scenario. It uses modern
C++ practices, such as std::unique_ptr for memory management and the override keyword for
clarity.

C++

#include <iostream>​
#include <memory>​
#include <vector>​
#include <string>​

// Product interface​
class Document {​
public:​
virtual ~Document() = default;​
virtual void open() = 0;​
virtual void save() = 0;​
};​

// ConcreteProduct A​
class TextDocument : public Document {​
public:​
void open() override { std::cout << "Opening Text Document.\n"; }​
void save() override { std::cout << "Saving Text Document.\n"; }​
};​

// ConcreteProduct B​
class Spreadsheet : public Document {​
public:​
void open() override { std::cout << "Opening Spreadsheet.\n"; }​
void save() override { std::cout << "Saving Spreadsheet.\n"; }​
};​

// Creator​
class Application {​
protected:​
std::vector<std::unique_ptr<Document>> docs_;​
public:​
virtual ~Application() = default;​
// The Factory Method​
virtual std::unique_ptr<Document> createDocument() const = 0;​

void newDocument() {​
std::unique_ptr<Document> doc = createDocument();​
doc->open();​
docs_.push_back(std::move(doc));​
}​
};​

// ConcreteCreator A​
class TextEditorApplication : public Application {​
public:​
std::unique_ptr<Document> createDocument() const override {​
return std::make_unique<TextDocument>();​
}​
};​

// ConcreteCreator B​
class SpreadsheetApplication : public Application {​
public:​
std::unique_ptr<Document> createDocument() const override {​
return std::make_unique<Spreadsheet>();​
}​
};​

int main() {​
std::unique_ptr<Application> app;​
std::string config = "spreadsheet"; // This could come from a config file​

if (config == "text") {​
app = std::make_unique<TextEditorApplication>();​
} else {​
app = std::make_unique<SpreadsheetApplication>();​
}​

app->newDocument();​

return 0;​
}​

In this example, the main function acts as the client. It decides at runtime which Application to
create. The Application's newDocument method then calls the createDocument factory
method. Because createDocument is virtual, the call is dispatched to the concrete subclass
(TextEditorApplication or SpreadsheetApplication), which creates the appropriate document
type. The base Application class remains completely decoupled from the concrete document
classes.30

Consequences

●​ Pros:
○​ Loose Coupling: The Creator is not bound to concrete product classes, promoting
flexibility.30
○​ Extensibility (OCP): New product types can be introduced by adding new
ConcreteProduct and ConcreteCreator classes without modifying existing client or
creator code.28
○​ Centralized Creation Logic: The logic for creating an object is encapsulated within
the factory method, making the code cleaner and easier to maintain.30
●​ Cons:
○​ Increased Complexity: The pattern can lead to a parallel hierarchy of classes, one
for products and one for creators, which can add complexity to the overall design.

Chapter 4: Abstract Factory

Intent

The Abstract Factory pattern provides an interface for creating families of related or
dependent objects without specifying their concrete classes.6 It is often called a "factory of
factories" because it provides a way to group individual factory methods for a common
theme.3
Problem

Imagine developing a cross-platform application that must adapt its user interface (UI) to the
underlying operating system. For a consistent user experience, all UI elements—buttons,
scrollbars, windows—must match the OS's native look and feel. An application running on
Windows should create WindowsButton and WindowsScrollBar objects, while the same
application on macOS should create MacButton and MacScrollBar objects.

The challenge is to structure the application so that the client code that constructs the UI is
not littered with if-else or #ifdef statements to handle the different OS-specific classes.33 The
client should be able to create a whole family of widgets without being coupled to any
concrete widget class.

Structure and Participants

The Abstract Factory pattern solves this by defining an abstract factory for each family of
products.31
●​ AbstractFactory: (GUIFactory) Declares an interface with operations for creating each
type of abstract product (e.g., createButton(), createScrollBar()).
●​ ConcreteFactory: (WindowsFactory, MacFactory) Implements the operations to create
concrete product objects for a specific family.
●​ AbstractProduct: (Button, ScrollBar) Declares an interface for a type of product object.
●​ ConcreteProduct: (WindowsButton, MacButton, WindowsScrollBar, MacScrollBar)
Defines a product object to be created by the corresponding concrete factory and
implements the AbstractProduct interface.
●​ Client: Uses only the interfaces declared by AbstractFactory and AbstractProduct
classes.

C++ Implementation Deep Dive

This example implements the cross-platform UI toolkit scenario. The client code is configured
with a specific factory at runtime, and from that point on, it creates a consistent family of
products without knowing their concrete types.
C++

#include <iostream>​
#include <memory>​
#include <string>​

// AbstractProduct A​
class Button {​
public:​
virtual ~Button() = default;​
virtual void paint() const = 0;​
};​

// ConcreteProduct A1​
class WindowsButton : public Button {​
public:​
void paint() const override { std::cout << "Rendering a Windows button.\n"; }​
};​

// ConcreteProduct A2​
class MacButton : public Button {​
public:​
void paint() const override { std::cout << "Rendering a macOS button.\n"; }​
};​

// AbstractProduct B​
class ScrollBar {​
public:​
virtual ~ScrollBar() = default;​
virtual void paint() const = 0;​
};​

// ConcreteProduct B1​
class WindowsScrollBar : public ScrollBar {​
public:​
void paint() const override { std::cout << "Rendering a Windows scrollbar.\n"; }​
};​

// ConcreteProduct B2​
class MacScrollBar : public ScrollBar {​
public:​
void paint() const override { std::cout << "Rendering a macOS scrollbar.\n"; }​
};​

// AbstractFactory​
class GUIFactory {​
public:​
virtual ~GUIFactory() = default;​
virtual std::unique_ptr<Button> createButton() const = 0;​
virtual std::unique_ptr<ScrollBar> createScrollBar() const = 0;​
};​

// ConcreteFactory 1​
class WindowsFactory : public GUIFactory {​
public:​
std::unique_ptr<Button> createButton() const override {​
return std::make_unique<WindowsButton>();​
}​
std::unique_ptr<ScrollBar> createScrollBar() const override {​
return std::make_unique<WindowsScrollBar>();​
}​
};​

// ConcreteFactory 2​
class MacFactory : public GUIFactory {​
public:​
std::unique_ptr<Button> createButton() const override {​
return std::make_unique<MacButton>();​
}​
std::unique_ptr<ScrollBar> createScrollBar() const override {​
return std::make_unique<MacScrollBar>();​
}​
};​

// Client​
class Application {​
private:​
std::unique_ptr<GUIFactory> factory_;​
std::unique_ptr<Button> button_;​
public:​
Application(std::unique_ptr<GUIFactory> factory) : factory_(std::move(factory)) {}​

void createUI() {​
button_ = factory_->createButton();​
}​
void paint() {​
button_->paint();​
}​
};​

int main() {​
// Configuration determines which factory to use​
#ifdef _WIN32​
auto factory = std::make_unique<WindowsFactory>();​
#else​
auto factory = std::make_unique<MacFactory>();​
#endif​

Application app(std::move(factory));​
app.createUI();​
app.paint();​

return 0;​
}​

The client (Application) is initialized with a GUIFactory. It doesn't know or care if it's a
WindowsFactory or MacFactory. When it calls createButton(), the factory ensures the created
button is compatible with its family. This guarantees product consistency.31

Relations with Other Patterns

Abstract Factory is often compared to Factory Method. A key difference is that Factory
Method uses class inheritance (subclasses decide which object to create), whereas Abstract
Factory uses object composition (the client is composed with a factory object that creates
the products). An Abstract Factory often uses Factory Methods in its implementation to create
the concrete products.

Chapter 5: Builder
Intent

The Builder pattern separates the construction of a complex object from its representation,
allowing the same construction process to create different variations of the object.8

Problem

Constructing an object can become unwieldy when it has a large number of parameters,
especially if many of them are optional. A common anti-pattern, the "telescoping constructor,"
arises where you have multiple constructor overloads with different combinations of
parameters. This leads to code that is hard to read and maintain.35 For example, creating an
HTML element might involve setting its tag, content, and numerous optional attributes.

C++

// Telescoping constructor anti-pattern​


HTMLElement(std::string tag, std::string text);​
HTMLElement(std::string tag, std::string text, std::string id);​
HTMLElement(std::string tag, std::string text, std::string id, std::string css_class);​
//... and so on​

The Builder pattern provides a more flexible and readable solution by breaking down the
construction into a series of step-by-step method calls.

Structure and Participants

The pattern typically involves four main roles 37:


●​ Product: The complex object that is being constructed (e.g., HTMLElement).
●​ Builder: An abstract interface that specifies the steps for building the Product (e.g.,
set_tag, set_text, add_attribute).
●​ ConcreteBuilder: Implements the Builder interface, keeps track of the representation it's
creating, and provides a method to retrieve the final product.
●​ Director (Optional): A class that defines the order in which to execute the building
steps. It works with a Builder object to construct a product according to a predefined
algorithm. The Director is optional; the client can also control the builder directly.

C++ Implementation Deep Dive

This example demonstrates building an HTML element using a fluent interface, where builder
methods return a reference to *this to enable method chaining.

C++

#include <iostream>​
#include <string>​
#include <vector>​
#include <sstream>​

// Product​
class HTMLElement {​
public:​
std::string name, text;​
std::vector<std::pair<std::string, std::string>> attributes;​

friend std::ostream& operator<<(std::ostream& os, const HTMLElement& e) {​
os << "<" << e.name;​
for (const auto& attr : e.attributes) {​
os << " " << attr.first << "=\"" << attr.second << "\"";​
}​
os << ">" << e.text << "</" << e.name << ">";​
return os;​
}​
};​

// Builder​
class HTMLBuilder {​
protected:​
HTMLElement root;​
public:​
HTMLBuilder(const std::string& root_name) {​
root.name = root_name;​
}​

// Fluent interface for chaining​
HTMLBuilder& add_child(const std::string& child_name, const std::string& child_text) {​
// In a real implementation, this would build a tree.​
// For simplicity, we'll just append to the root's text.​
HTMLElement e{child_name, child_text};​
std::stringstream ss;​
ss << e;​
root.text += ss.str();​
return *this;​
}​

HTMLElement build() { return root; }​
};​

int main() {​
HTMLBuilder builder{"ul"};​
builder.add_child("li", "hello").add_child("li", "world");​

HTMLElement list = builder.build();​
std::cout << list << std::endl; // Output: <ul><li>hello</li><li>world</li></ul>​

return 0;​
}​

Here, the HTMLBuilder acts as both the Builder and, in a simple way, the Director. The client
directly calls the construction steps (add_child). The chained calls
(builder.add_child(...).add_child(...)) provide a highly readable and expressive way to construct
the object.35

Consequences

●​ Pros:
○​ Finer Control: The step-by-step construction process provides finer control over the
final product.
○​ Improved Readability: Named methods for setting parameters are more readable
than a long list of constructor arguments.
○​ Isolation of Logic: Construction logic is isolated from the product's business logic.39
○​ Varying Representations: The same construction process can be used to create
different representations of the product by using different ConcreteBuilders.
●​ Cons:
○​ Verbosity: The pattern requires creating a new Builder class for each type of
Product, which can increase the overall number of classes in the system.38

Chapter 6: Prototype

Intent

The Prototype pattern specifies the kinds of objects to create using a prototypical instance
and creates new objects by copying this prototype.8

Problem

Object creation can be a resource-intensive process, especially when it involves complex


initializations, database queries, or network communications.40 If you need to create many
similar objects, repeating this expensive creation process for each one is inefficient. It is often
more performant to create a single, fully initialized object (the prototype) and then create
copies of it whenever a new instance is needed.41

Structure and Participants

The structure of the Prototype pattern is relatively simple 42:


●​ Prototype: Declares an interface for cloning itself. In C++, this is typically a virtual clone()
method.
●​ ConcretePrototype: Implements the clone() method to create a copy of itself.
●​ Client: Creates new objects by asking a prototype to clone itself.
C++ Implementation Deep Dive

This example uses a simple Shape hierarchy. We create prototype objects for a circle and a
rectangle, which can then be cloned to create new shape instances.

A critical aspect of implementing the clone() method in C++ is handling the difference
between a shallow copy and a deep copy. A shallow copy simply copies the values of an
object's members. If a member is a pointer, only the pointer address is copied, not the data it
points to. This can lead to multiple objects sharing and potentially corrupting the same
underlying data. A deep copy, on the other hand, creates new copies of any dynamically
allocated memory, ensuring that the clone is a completely independent object. When
implementing the Prototype pattern, a deep copy is almost always required.42

C++

#include <iostream>​
#include <memory>​
#include <string>​
#include <unordered_map>​

// Prototype​
class Shape {​
public:​
virtual ~Shape() = default;​
virtual std::unique_ptr<Shape> clone() const = 0;​
virtual void draw() const = 0;​
};​

// ConcretePrototype A​
class Circle : public Shape {​
private:​
int radius;​
public:​
Circle(int r) : radius(r) {}​

// Copy constructor for deep copy​
Circle(const Circle& other) {​
this->radius = other.radius;​
}​

std::unique_ptr<Shape> clone() const override {​
return std::make_unique<Circle>(*this);​
}​

void draw() const override {​
std::cout << "Drawing a circle with radius " << radius << ".\n";​
}​
};​

// ConcretePrototype B​
class Rectangle : public Shape {​
private:​
int width, height;​
public:​
Rectangle(int w, int h) : width(w), height(h) {}​

std::unique_ptr<Shape> clone() const override {​
return std::make_unique<Rectangle>(*this);​
}​

void draw() const override {​
std::cout << "Drawing a rectangle with width " << width << " and height " << height << ".\n";​
}​
};​

// Prototype Registry (optional but common)​
class ShapeRegistry {​
private:​
std::unordered_map<std::string, std::unique_ptr<Shape>> prototypes;​
public:​
ShapeRegistry() {​
prototypes["circle"] = std::make_unique<Circle>(10);​
prototypes["rectangle"] = std::make_unique<Rectangle>(20, 15);​
}​

std::unique_ptr<Shape> createShape(const std::string& key) {​
return prototypes[key]->clone();​
}​
};​

int main() {​
ShapeRegistry registry;​

auto my_circle = registry.createShape("circle");​
auto my_rectangle = registry.createShape("rectangle");​

my_circle->draw();​
my_rectangle->draw();​

return 0;​
}​

The ShapeRegistry acts as a manager for the prototypes. The client code can request a new
shape by its key, and the registry finds the corresponding prototype and calls its clone()
method.42 The use of

std::make_unique<Circle>(*this) in the clone method leverages the copy constructor to ensure


a proper copy is made.

Consequences

●​ Pros:
○​ Performance: Can significantly improve performance by avoiding the cost of
repeated object creation from scratch.8
○​ Flexibility: New concrete product classes can be added and registered with the
client at runtime without changing the client's code.
○​ Simplicity: Hides the concrete product classes and their creation logic from the
client.
●​ Cons:
○​ Complexity of Cloning: Implementing clone() can be complex for objects that have
circular references or complex internal structures.
○​ Requires Deep Copy Implementation: Every class in the prototype hierarchy must
implement the clone() method and correctly handle deep copying.

Part II: Structural Design Patterns

Structural patterns focus on how classes and objects are composed to form larger, more
robust, and flexible structures.5 They are the architectural glue of a system, helping to
manage dependencies and simplify complex relationships. A key theme among these patterns
is the management of interfaces and object composition to reduce coupling and enhance
maintainability. For instance, the Adapter pattern is used reactively to make incompatible
interfaces work together, while the Bridge pattern is used proactively to prevent such
incompatibilities from arising in the first place. The Facade pattern simplifies a complex
subsystem by providing a single entry point, whereas the Proxy adds a layer of indirection to
control access to an object. Understanding these patterns provides a toolkit for shaping a
system's architecture effectively.

Pattern Intent Common C++ Use Case

Adapter Convert the interface of a Integrating a third-party


class into another interface library or legacy
clients expect, allowing component with a modern
classes with incompatible system that expects a
interfaces to work together. different interface.

Bridge Decouple an abstraction Creating a GUI framework


from its implementation so that must run on multiple
that the two can vary platforms (Windows,
independently. macOS), where the UI
controls (Abstraction) are
separate from the
platform-specific drawing
APIs (Implementation).

Composite Compose objects into tree Representing a file system


structures to represent (folders containing files
part-whole hierarchies, and other folders) or a GUI
allowing clients to treat layout (panels containing
individual objects and buttons and other panels).
compositions uniformly.

Decorator Attach additional Adding features like


responsibilities to an object borders, scrollbars, or
dynamically. Decorators logging to a UI component
provide a flexible or a data stream without
alternative to subclassing modifying the original
for extending functionality. object's class.
Facade Provide a unified interface Creating a simple API to
to a set of interfaces in a start and stop a complex
subsystem. Facade defines home theater system
a higher-level interface that involving a TV, amplifier,
makes the subsystem and DVD player.
easier to use.

Flyweight Use sharing to support Rendering thousands of


large numbers of trees in a forest, where the
fine-grained objects mesh and texture (intrinsic)
efficiently by separating are shared, but the position
intrinsic (shared) state from and scale (extrinsic) are
extrinsic (unique) state. unique.

Proxy Provide a surrogate or Implementing lazy loading


placeholder for another for a high-resolution
object to control access to image, access control for a
it. sensitive object, or a local
representative for a remote
service.

Chapter 7: Adapter

Intent

The Adapter pattern allows objects with incompatible interfaces to collaborate. It acts as a
wrapper that converts the interface of one class into an interface that another client
expects.20

Problem

Imagine integrating a new third-party analytics library into an existing application. The
application's code expects to work with an ILogger interface that has a
logMessage(std::string) method. However, the new analytics library provides a class
SuperAnalytics with a completely different interface, such as submitEvent(std::string
event_name, std::string event_data). Modifying either the existing application code or the
third-party library is undesirable or impossible.

Structure and Participants

The Adapter pattern bridges this gap with a middle-layer class.45


●​ Target: The interface that the client code uses and expects (ILogger).
●​ Client: The class that interacts with objects conforming to the Target interface.
●​ Adaptee: The existing class with an incompatible interface that needs adapting
(SuperAnalytics).
●​ Adapter: A class that implements the Target interface and internally wraps an instance of
the Adaptee. It translates calls from the Target interface into calls on the Adaptee's
interface.

There are two main implementations of the Adapter pattern:


1.​ Object Adapter: The Adapter holds an instance of the Adaptee (using composition). This
is the more common and flexible approach.
2.​ Class Adapter: The Adapter inherits from both the Target interface and the Adaptee
implementation (using multiple inheritance). This approach is less common and only
possible in languages like C++ that support multiple inheritance.44

C++ Implementation Deep Dive (Object Adapter)

This example demonstrates the Object Adapter pattern to make the SuperAnalytics library
work with the application's ILogger interface.

C++

#include <iostream>​
#include <string>​
#include <algorithm>​

// Target interface​
class ILogger {​
public:​
virtual ~ILogger() = default;​
virtual void logMessage(const std::string& message) const = 0;​
};​

// Adaptee​
class SuperAnalytics {​
public:​
void submitEvent(const std::string& event_name, const std::string& event_data) const {​
std::cout << "SuperAnalytics: Event '" << event_name << "' with data '" << event_data << "'
submitted.\n";​
}​
};​

// Adapter​
class AnalyticsAdapter : public ILogger {​
private:​
SuperAnalytics* adaptee_;​
public:​
AnalyticsAdapter(SuperAnalytics* adaptee) : adaptee_(adaptee) {}​

void logMessage(const std::string& message) const override {​
// Translate the logMessage call into a submitEvent call​
adaptee_->submitEvent("log", message);​
}​
};​

// Client code​
void clientCode(const ILogger& logger) {​
logger.logMessage("This is a test message.");​
}​

int main() {​
SuperAnalytics* analytics_service = new SuperAnalytics();​
AnalyticsAdapter* adapter = new AnalyticsAdapter(analytics_service);​

std::cout << "Client is using the adapter:\n";​
clientCode(*adapter);​

delete adapter;​
delete analytics_service;​
return 0;​
}​

The AnalyticsAdapter implements the ILogger interface. Its logMessage method takes the
simple string message and transforms it into the format expected by SuperAnalytics, calling
submitEvent on the wrapped adaptee_ object. The clientCode can now work with the analytics
service through the familiar ILogger interface, completely unaware of the adaptation
happening behind the scenes.46

Consequences

●​ Pros:
○​ Reusability: Allows reuse of existing classes even if their interfaces do not match.46
○​ Single Responsibility: The responsibility of interface conversion is isolated within
the Adapter class, keeping both the client and adaptee code clean.
○​ Open/Closed Principle: New adapters can be introduced to support different
adaptees without modifying the client code.48
●​ Cons:
○​ Increased Complexity: Introduces an additional layer of indirection and a new class,
which can add complexity to the system.

Chapter 8: Bridge

Intent

The Bridge pattern decouples an abstraction from its implementation so that the two can vary
independently.20 It involves splitting a large class or a set of closely related classes into two
separate hierarchies—Abstraction and Implementation.
Problem

Consider a set of geometric Shape classes (Circle, Square) that need to be rendered using
different drawing APIs (DrawingAPI_V1, DrawingAPI_V2). A naive approach would be to create
a subclass for each combination, such as Circle_V1, Circle_V2, Square_V1, and Square_V2. This
leads to a "Cartesian product" explosion of classes.50 If a new shape (

Triangle) or a new API (DrawingAPI_V3) is added, the number of classes grows exponentially,
making the system rigid and difficult to maintain.51

Structure and Participants

The Bridge pattern solves this by creating two independent inheritance hierarchies 49:
●​ Abstraction: (Shape) Defines the high-level interface that the client uses. It contains a
reference to an Implementation object.
●​ RefinedAbstraction: (Circle, Square) Extends the Abstraction interface to provide
specific variations.
●​ Implementation: (DrawingAPI) Defines the interface for the implementation classes. This
interface provides the primitive operations needed by the Abstraction.
●​ ConcreteImplementation: (DrawingAPI_V1, DrawingAPI_V2) Implements the
Implementation interface, providing platform-specific or API-specific code.

The key is that the Abstraction has-a Implementation (composition), rather than is-a
Implementation (inheritance).

C++ Implementation Deep Dive

This example implements the Shape and DrawingAPI scenario, demonstrating how the two
hierarchies can evolve independently.

C++
#include <iostream>​
#include <memory>​

// Implementation interface​
class DrawingAPI {​
public:​
virtual ~DrawingAPI() = default;​
virtual void drawCircle(double x, double y, double radius) = 0;​
};​

// ConcreteImplementation A​
class DrawingAPI_V1 : public DrawingAPI {​
public:​
void drawCircle(double x, double y, double radius) override {​
std::cout << "APIv1.circle at " << x << ":" << y << " radius " << radius << std::endl;​
}​
};​

// ConcreteImplementation B​
class DrawingAPI_V2 : public DrawingAPI {​
public:​
void drawCircle(double x, double y, double radius) override {​
std::cout << "APIv2.circle at " << x << ":" << y << " radius " << radius << std::endl;​
}​
};​

// Abstraction​
class Shape {​
protected:​
DrawingAPI& drawingAPI;​
public:​
Shape(DrawingAPI& api) : drawingAPI(api) {}​
virtual ~Shape() = default;​
virtual void draw() = 0;​
virtual void resize(double pct) = 0;​
};​

// RefinedAbstraction​
class CircleShape : public Shape {​
private:​
double x, y, radius;​
public:​
CircleShape(double x, double y, double r, DrawingAPI& api) : Shape(api), x(x), y(y), radius(r) {}​

void draw() override {​
drawingAPI.drawCircle(x, y, radius);​
}​
void resize(double pct) override {​
radius *= pct;​
}​
};​

int main() {​
DrawingAPI_V1 api1;​
DrawingAPI_V2 api2;​

CircleShape circle1(1, 2, 3, api1);​
CircleShape circle2(5, 7, 11, api2);​

circle1.draw();​
circle2.draw();​

return 0;​
}​

Here, Shape is the abstraction and DrawingAPI is the implementation. A CircleShape can be
constructed with any object that conforms to the DrawingAPI interface. We can now add new
shapes (e.g., SquareShape) or new drawing APIs (e.g., OpenGL_API) without affecting the
other hierarchy.52

Relations with Other Patterns

Bridge is often confused with Adapter, but they have different intents. Adapter is used
retrospectively to make incompatible interfaces work together. Bridge is designed up-front to
allow the abstraction and implementation to vary independently.45 The structure of Bridge is
similar to State and Strategy, as all are based on composition; however, they solve different
problems.51

Chapter 9: Composite
Intent

The Composite pattern composes objects into tree structures to represent part-whole
hierarchies. It lets clients treat individual objects (leaves) and compositions of objects
(composites) uniformly.20

Problem

Many applications require dealing with objects that are structured as a tree. For example, a
graphics application needs to handle simple shapes like lines and circles (primitives) as well as
complex shapes that are groups of other shapes (composites). The client code should be able
to perform operations like draw() or move() on any graphical object, regardless of whether it
is a simple primitive or a complex group, without needing complex if-else logic to differentiate
between them.

Structure and Participants

The pattern achieves this uniform treatment through a shared interface 54:
●​ Component: (Graphic) The abstract base class or interface for all objects in the
composition. It declares the interface for operations common to all components, such as
draw(). It may also declare an interface for managing child components (add, remove).
●​ Leaf: (Circle, Line) Represents the primitive objects in the composition. A Leaf has no
children, so its add and remove methods are typically empty.
●​ Composite: (Picture) Represents a composite object that can have children. It stores
child components (usually in a list or vector) and implements the child-management
operations. Its implementation of draw() typically iterates over its children and calls
draw() on each of them.
●​ Client: Manipulates objects in the composition through the Component interface.

C++ Implementation Deep Dive


This example implements a simple graphics system where a Picture can contain Circles and
other Pictures.

C++

#include <iostream>​
#include <vector>​
#include <memory>​
#include <string>​
#include <algorithm>​

// Component​
class Graphic {​
public:​
virtual ~Graphic() = default;​
virtual void draw() const = 0;​
virtual void add(Graphic* g) {} // Default empty implementation​
virtual void remove(Graphic* g) {}​
};​

// Leaf​
class Circle : public Graphic {​
public:​
void draw() const override {​
std::cout << "Drawing a Circle.\n";​
}​
};​

// Composite​
class Picture : public Graphic {​
private:​
std::vector<Graphic*> children;​
public:​
~Picture() {​
for (Graphic* child : children) {​
delete child;​
}​
}​

void draw() const override {​
std::cout << "Drawing a Picture:\n";​
for (const auto& child : children) {​
child->draw();​
}​
}​

void add(Graphic* g) override {​
children.push_back(g);​
}​

void remove(Graphic* g) override {​
children.erase(std::remove(children.begin(), children.end(), g), children.end());​
}​
};​

int main() {​
// Client code works with all components via the base interface​
Picture* main_pic = new Picture();​
main_pic->add(new Circle());​

Picture* sub_pic = new Picture();​
sub_pic->add(new Circle());​
sub_pic->add(new Circle());​

main_pic->add(sub_pic);​
main_pic->draw();​

delete main_pic;​
return 0;​
}​

The client code can build a complex tree structure (main_pic containing a Circle and a
sub_pic, which itself contains two Circles) and then call draw() on the root of the tree. The
Picture::draw() method recursively calls draw() on all its children, demonstrating the uniform
treatment of both leaf (Circle) and composite (Picture) objects.57

Consequences

●​ Pros:
○​ Uniformity: Client code is simplified because it doesn't need to distinguish between
simple and composite objects.
○​ Extensibility: It is easy to add new kinds of Component classes (either new leaves or
new composites).
○​ Hierarchical Structures: The pattern makes it easy to work with complex, recursive
tree structures.
●​ Cons:
○​ "Fat" Interface: The Component interface can become overly general. Leaf classes
are forced to have methods like add and remove that they don't use.
○​ Type Safety: It can be difficult to restrict the types of components that can be
added to a composite at compile time.

Chapter 10: Decorator

Intent

The Decorator pattern allows for adding new behaviors or responsibilities to an object
dynamically by placing it inside a special wrapper object, called a decorator.20 This provides a
flexible alternative to subclassing for extending functionality.5

Problem

Imagine a text editor application with a TextView component that displays text. You need to
add features like borders and scrollbars. One approach is to use inheritance, creating
subclasses like TextViewWithBorder, TextViewWithScrollbar, and even
TextViewWithBorderAndScrollbar. This leads to a class explosion, where each combination of
features requires a new subclass. Furthermore, this approach is static; features are added at
compile time and cannot be changed at runtime.

Structure and Participants


The Decorator pattern solves this using object composition 58:
●​ Component: (VisualComponent) The abstract interface for both the objects to be
decorated and the decorators themselves.
●​ ConcreteComponent: (TextView) The core object to which new responsibilities can be
attached.
●​ Decorator: An abstract class that also conforms to the Component interface. It contains
a reference to a Component object (the "wrappee"). Its default implementation of
operations is to delegate the call to the wrapped component.
●​ ConcreteDecorator: (BorderDecorator, ScrollDecorator) Implements the specific
additional functionality. It adds its behavior either before or after delegating the call to
the wrapped component.

C++ Implementation Deep Dive

This example implements the TextView scenario, showing how decorators can be "stacked" on
top of each other.

C++

#include <iostream>​
#include <memory>​
#include <string>​

// Component​
class VisualComponent {​
public:​
virtual ~VisualComponent() = default;​
virtual void draw() const = 0;​
};​

// ConcreteComponent​
class TextView : public VisualComponent {​
public:​
void draw() const override {​
std::cout << "Drawing TextView content.";​
}​
};​

// Decorator base class​
class Decorator : public VisualComponent {​
protected:​
std::unique_ptr<VisualComponent> component_;​
public:​
Decorator(std::unique_ptr<VisualComponent> component) :
component_(std::move(component)) {}​

void draw() const override {​
if (component_) {​
component_->draw();​
}​
}​
};​

// ConcreteDecorator A​
class BorderDecorator : public Decorator {​
public:​
BorderDecorator(std::unique_ptr<VisualComponent> component) :
Decorator(std::move(component)) {}​

void draw() const override {​
std::cout << "";​
}​
};​

// ConcreteDecorator B​
class ScrollDecorator : public Decorator {​
public:​
ScrollDecorator(std::unique_ptr<VisualComponent> component) :
Decorator(std::move(component)) {}​

void draw() const override {​
std::cout << "<Scrollbar: ";​
Decorator::draw();​
std::cout << ">";​
}​
};​

int main() {​
// Create a TextView​
auto textView = std::make_unique<TextView>();​

// Decorate it with a border​
auto borderedView = std::make_unique<BorderDecorator>(std::move(textView));​

// Decorate the bordered view with a scrollbar​
auto scrolledAndBorderedView =
std::make_unique<ScrollDecorator>(std::move(borderedView));​

std::cout << "Drawing the fully decorated component:\n";​
scrolledAndBorderedView->draw();​
std::cout << "\n";​

return 0;​
}​

In main, a TextView is first wrapped by a BorderDecorator, and then the resulting object is
wrapped by a ScrollDecorator. When draw() is called on the outermost decorator, the call is
passed down the chain. ScrollDecorator::draw() calls BorderDecorator::draw(), which in turn
calls TextView::draw(). Each decorator adds its own behavior around the delegated call,
resulting in a "stacked" output: <Scrollbar:>.58

Consequences

●​ Pros:
○​ Flexibility: New features can be added to objects at runtime, and multiple decorators
can be combined.60
○​ Avoids Feature-Laden Superclasses: Functionality is divided among several
smaller decorator classes instead of being concentrated in a single large class.
○​ Composition over Inheritance: The pattern favors a more flexible object
composition model over a rigid static inheritance model.60
●​ Cons:
○​ Many Small Objects: A design using Decorator can result in a system with a large
number of small, similar-looking objects, which can be hard to debug and
understand.59
○​ Complexity: The initial setup with multiple layers of wrapping can be more complex
than simple inheritance.
Chapter 11: Facade

Intent

The Facade pattern provides a simplified, unified interface to a complex subsystem of classes,
a library, or a framework. It defines a higher-level interface that makes the subsystem easier
to use.20

Problem

Modern software systems often rely on complex libraries and frameworks with dozens of
classes and intricate dependencies. To perform a simple task, a client might need to initialize
multiple objects, manage their lifecycles, and call their methods in a specific order.64 This
tightly couples the client code to the subsystem's internal implementation details, making the
client code complex and difficult to maintain. If the subsystem is updated or replaced, the
client code must be extensively rewritten.

Structure and Participants

The Facade pattern introduces a single class to shield the client from this complexity 62:
●​ Facade: This class knows which subsystem classes are responsible for a request and
delegates client requests to the appropriate subsystem objects. It provides a simple,
high-level interface for common tasks.
●​ Subsystem Classes: These classes implement the low-level functionality of the
subsystem. They have no knowledge of the Facade and are not aware that they are part
of a larger system from the Facade's perspective.
●​ Client: Communicates with the subsystem by sending requests to the Facade, rather
than interacting with the subsystem classes directly.
C++ Implementation Deep Dive

This example models a simplified home theater system. To watch a movie, the client would
normally have to interact with the Amplifier, DvdPlayer, and Projector classes individually. The
HomeTheaterFacade simplifies this process into a single watchMovie() call.

C++

#include <iostream>​
#include <string>​
#include <memory>​

// Subsystem classes​
class Amplifier {​
public:​
void on() { std::cout << "Amplifier on\n"; }​
void setDvd() { std::cout << "Amplifier setting DVD player\n"; }​
void setVolume(int level) { std::cout << "Amplifier setting volume to " << level << "\n"; }​
void off() { std::cout << "Amplifier off\n"; }​
};​

class DvdPlayer {​
public:​
void on() { std::cout << "DVD Player on\n"; }​
void play(const std::string& movie) { std::cout << "DVD Player playing \"" << movie << "\"\n"; }​
void off() { std::cout << "DVD Player off\n"; }​
};​

class Projector {​
public:​
void on() { std::cout << "Projector on\n"; }​
void wideScreenMode() { std::cout << "Projector in widescreen mode\n"; }​
void off() { std::cout << "Projector off\n"; }​
};​

// Facade​
class HomeTheaterFacade {​
private:​
std::shared_ptr<Amplifier> amp;​
std::shared_ptr<DvdPlayer> dvd;​
std::shared_ptr<Projector> projector;​
public:​
HomeTheaterFacade(std::shared_ptr<Amplifier> a, std::shared_ptr<DvdPlayer> d,
std::shared_ptr<Projector> p)​
: amp(a), dvd(d), projector(p) {}​

void watchMovie(const std::string& movie) {​
std::cout << "Get ready to watch a movie...\n";​
projector->on();​
projector->wideScreenMode();​
amp->on();​
amp->setDvd();​
amp->setVolume(5);​
dvd->on();​
dvd->play(movie);​
}​

void endMovie() {​
std::cout << "\nShutting movie theater down...\n";​
dvd->off();​
amp->off();​
projector->off();​
}​
};​

int main() {​
auto amp = std::make_shared<Amplifier>();​
auto dvd = std::make_shared<DvdPlayer>();​
auto projector = std::make_shared<Projector>();​

HomeTheaterFacade homeTheater(amp, dvd, projector);​
homeTheater.watchMovie("Raiders of the Lost Ark");​
homeTheater.endMovie();​

return 0;​
}​

The HomeTheaterFacade encapsulates all the steps required to start and stop the movie. The
client code in main is now much simpler and is decoupled from the individual subsystem
components. It only needs to interact with the facade.65

Consequences

●​ Pros:
○​ Simplified Interface: Provides a simple entry point to a complex system, making it
easier to use.65
○​ Decoupling: Decouples clients from the subsystem's components, which promotes
modularity and allows the subsystem to be modified or replaced with minimal impact
on the client code.65
○​ Layering: Helps to structure a system into layers, with the Facade acting as the entry
point to each layer.64
●​ Cons:
○​ Potential God Object: A Facade can become a "god object" coupled to all classes
of an application if not designed carefully.
○​ Limited Functionality: The Facade provides a simplified interface, which may not
expose all the functionality of the subsystem. Clients who need more advanced
features may still need to access the subsystem classes directly.

Chapter 12: Flyweight

Intent

The Flyweight pattern allows a program to support vast quantities of objects by minimizing
memory consumption. It achieves this by sharing common parts of an object's state between
multiple objects, rather than storing all data in each object.20

Problem

Some applications require creating a huge number of similar objects, which can lead to
excessive memory usage. For example, in a word processor, each character has properties
like font, size, and style, but also a position on the page. If every single character object
stored its font data, the memory footprint would be enormous, as most characters in a
document share the same font.

Structure and Participants

The Flyweight pattern addresses this by separating an object's state into two parts 67:
●​ Intrinsic State: This is the data that is constant and can be shared among many objects
(e.g., the font, size, and style of a character). This state is stored within the Flyweight
object.
●​ Extrinsic State: This is the data that is unique to each object and cannot be shared (e.g.,
the character's position and the character code itself). This state is passed to the
Flyweight's methods by the client.

The key participants are:


●​ Flyweight: Declares an interface through which flyweights can receive and act on
extrinsic state.
●​ ConcreteFlyweight: Stores the intrinsic state. These objects must be sharable.
●​ FlyweightFactory: Creates and manages flyweight objects. When a client requests a
flyweight, the factory returns an existing instance if one is available or creates a new one.
This ensures that flyweights are shared correctly.
●​ Client: Maintains references to flyweights and stores the extrinsic state.

C++ Implementation Deep Dive

This example models the rendering of trees in a forest. Each Tree has a unique position (x, y),
which is its extrinsic state. However, the TreeType (name, color, texture) is shared among
many trees and represents the intrinsic state.

C++

#include <iostream>​
#include <string>​
#include <vector>​
#include <unordered_map>​
#include <memory>​

// The Flyweight class containing the intrinsic state​
class TreeType {​
private:​
std::string name;​
std::string color;​
public:​
TreeType(const std::string& n, const std::string& c) : name(n), color(c) {}​

void draw(int x, int y) const {​
std::cout << "Drawing a " << color << " " << name << " tree at (" << x << ", " << y << ").\n";​
}​
};​

// The Flyweight Factory​
class TreeFactory {​
private:​
std::unordered_map<std::string, std::shared_ptr<TreeType>> treeTypes;​
public:​
std::shared_ptr<TreeType> getTreeType(const std::string& name, const std::string& color) {​
std::string key = name + "_" + color;​
if (treeTypes.find(key) == treeTypes.end()) {​
std::cout << "Creating new TreeType: " << key << "\n";​
treeTypes[key] = std::make_shared<TreeType>(name, color);​
}​
return treeTypes[key];​
}​
};​

// The Context class that uses the flyweights​
class Tree {​
private:​
int x, y; // Extrinsic state​
std::shared_ptr<TreeType> type; // Pointer to the Flyweight​
public:​
Tree(int x, int y, std::shared_ptr<TreeType> type) : x(x), y(y), type(type) {}​

void draw() const {​
type->draw(x, y);​
}​
};​

// Client​
class Forest {​
private:​
TreeFactory factory;​
std::vector<Tree> trees;​
public:​
void plantTree(int x, int y, const std::string& name, const std::string& color) {​
auto type = factory.getTreeType(name, color);​
trees.emplace_back(x, y, type);​
}​

void draw() const {​
for (const auto& tree : trees) {​
tree.draw();​
}​
}​
};​

int main() {​
Forest forest;​
forest.plantTree(10, 20, "Oak", "Green");​
forest.plantTree(50, 30, "Pine", "Dark Green");​
forest.plantTree(100, 80, "Oak", "Green"); // Reuses existing TreeType​

forest.draw();​
return 0;​
}​

The TreeFactory ensures that for every unique combination of name and color, only one
TreeType object is created. The Forest plants multiple Tree objects, each with its own
coordinates, but they share TreeType objects. When the third tree ("Oak", "Green") is planted,
the factory recognizes that this type already exists and returns the shared instance, saving
memory.68

Consequences

●​ Pros:
○​ Memory Efficiency: Can drastically reduce the number of objects in memory,
especially when there are many instances with shared state.68
○​ Performance: Reusing objects can be faster than creating new ones.
●​ Cons:
○​ Increased Complexity: The code becomes more complex due to the separation of
intrinsic and extrinsic state and the need for a factory to manage the flyweight
objects.68
○​ Runtime Cost: There is a small runtime cost associated with retrieving or computing
the extrinsic state.

Chapter 13: Proxy

Intent

The Proxy pattern provides a surrogate or placeholder for another object to control access to
it. This allows for additional logic to be executed before or after the request is forwarded to
the original object.20

Problem

There are several scenarios where direct access to an object is not desirable or practical.
●​ Lazy Initialization (Virtual Proxy): An object might be very resource-intensive to create
(e.g., loading a high-resolution image from disk). We may want to delay its creation until it
is actually needed.69
●​ Access Control (Protection Proxy): We may want to control access to an object based
on the client's permissions. For example, some users may have read-only access while
others have read-write access.70
●​ Remote Access (Remote Proxy): The object may reside in a different address space or
on a remote server. The proxy acts as a local representative, handling the network
communication.
●​ Logging/Caching: The proxy can log requests to the object or cache the results of
expensive operations.
Structure and Participants

The Proxy pattern ensures that the proxy and the real object share the same interface, making
them interchangeable from the client's perspective.72
●​ Subject: An interface that is common to both the RealSubject and the Proxy. This allows
the client to work with the Proxy as if it were the RealSubject.
●​ RealSubject: The actual object that the proxy represents. It contains the core business
logic.
●​ Proxy: Maintains a reference to the RealSubject. It implements the Subject interface and
can perform additional tasks (like access control or lazy loading) before or after
forwarding the request to the RealSubject.
●​ Client: Interacts with the Subject interface, unaware of whether it is communicating with
the RealSubject or a Proxy.

C++ Implementation Deep Dive (Protection Proxy)

This example demonstrates a protection proxy that controls access to a SharedFolder.

C++

#include <iostream>​
#include <string>​

// Subject interface​
class IFolder {​
public:​
virtual ~IFolder() = default;​
virtual void performReadWrite() = 0;​
};​

// RealSubject​
class SharedFolder : public IFolder {​
public:​
void performReadWrite() override {​
std::cout << "Performing read/write operations on the shared folder.\n";​
}​
};​

// User class for demonstrating access control​
class User {​
public:​
std::string username;​
std::string role;​
};​

// Proxy​
class FolderProxy : public IFolder {​
private:​
std::unique_ptr<SharedFolder> real_folder_;​
User user_;​
public:​
FolderProxy(const User& user) : user_(user) {}​

void performReadWrite() override {​
if (user_.role == "Admin" |​

| user_.role == "Developer") {​
// Lazy initialization​
if (!real_folder_) {​
real_folder_ = std::make_unique<SharedFolder>();​
}​
std::cout << "Proxy: Access granted for user " << user_.username << ".\n";​
real_folder_->performReadWrite();​
} else {​
std::cout << "Proxy: Access denied for user " << user_.username << ". Insufficient
privileges.\n";​
}​
}​
};​

int main() {​
User admin_user{"AdminUser", "Admin"};​
User guest_user{"GuestUser", "Guest"};​

FolderProxy admin_proxy(admin_user);​
FolderProxy guest_proxy(guest_user);​

std::cout << "Client trying to access with admin credentials:\n";​
admin_proxy.performReadWrite();​

std::cout << "\nClient trying to access with guest credentials:\n";​
guest_proxy.performReadWrite();​

return 0;​
}​

The FolderProxy checks the User's role before granting access. If the user has the appropriate
role, it creates the SharedFolder object (if it doesn't exist already) and delegates the call. If
not, it denies access. The client interacts with both proxies through the IFolder interface, but
the behavior changes based on the access control logic within the proxy.73

Consequences

●​ Pros:
○​ Controlled Access: The proxy can control access to the real object, adding a layer
of security or management.
○​ Lazy Initialization: Can improve performance by delaying the creation of expensive
objects.
○​ Separation of Concerns: Additional functionalities like logging, caching, or
networking are handled by the proxy, keeping the RealSubject focused on its core
business logic.
●​ Cons:
○​ Increased Indirection: The additional layer of the proxy can add complexity and a
slight performance overhead to every call.
○​ Interface Duplication: The proxy must mirror the interface of the RealSubject, which
can lead to code duplication if the interface is large.

Part III: Behavioral Design Patterns

Behavioral patterns are concerned with algorithms and the assignment of responsibilities
between objects. They describe patterns of communication, making interactions flexible and
loosely coupled.4 A key theme is the encapsulation of behavior in objects, allowing that
behavior to be changed or composed dynamically. For example, the State and Strategy
patterns are structurally similar, both using composition to alter an object's behavior. However,
their intents differ: Strategy focuses on

how an object performs a task (the algorithm), often selected by the client, while State
focuses on what an object does based on its internal condition, with state transitions often
managed internally. Similarly, the Command and Memento patterns often work in synergy;
Command encapsulates a request, and Memento can be used to save the state before the
command is executed, enabling a robust undo/redo mechanism.

Pattern Intent Common C++ Use Case

Chain of Responsibility Avoid coupling the sender A pipeline for processing


of a request to its receiver events in a GUI, where a
by giving more than one click event might be
object a chance to handle handled by a button, its
the request. parent panel, or the main
window.

Command Encapsulate a request as Implementing undo/redo


an object, thereby functionality in a text
parameterizing clients with editor, or queueing actions
different requests, and for a game character.
supporting undoable
operations.

Interpreter Given a language, define a Parsing and evaluating a


representation for its simple language, like a
grammar along with an search query syntax or a
interpreter that uses the mathematical expression.
representation to interpret
sentences.

Iterator Provide a way to access the Traversing elements in a


elements of an aggregate custom collection class in a
object sequentially without way that is compatible with
exposing its underlying C++'s range-based for
representation. loops.

Mediator Define an object that A dialog box in a GUI where


encapsulates how a set of widgets (buttons, text
objects interact, promoting fields) communicate
loose coupling by through the dialog rather
centralizing than directly with each
communication. other.

Memento Without violating Implementing a "save


encapsulation, capture and game" feature or the
externalize an object's state-saving part of an
internal state so that it can undo/redo mechanism.
be restored to this state
later.

Observer Define a one-to-many A spreadsheet application


dependency so that when where changing a cell's
one object (subject) value (subject)
changes state, all its automatically updates all
dependents (observers) cells that depend on it
are notified. (observers).

State Allow an object to alter its Modeling the states of a


behavior when its internal network connection
state changes. The object (Connecting, Connected,
will appear to change its Disconnected) or the states
class. of a traffic light.

Strategy Define a family of Allowing a sorting function


algorithms, encapsulate to use different sorting
each one, and make them algorithms (QuickSort,
interchangeable. MergeSort) at runtime.

Template Method Define the skeleton of an A framework for data


algorithm in an operation, processing where the
deferring some steps to overall algorithm (read,
subclasses. process, write) is fixed, but
the specific data formats
are defined by subclasses.

Visitor Represent an operation to Adding new operations


be performed on the (e.g., export to XML, type
elements of an object checking) to a complex
structure. Visitor lets you object structure like an
define a new operation Abstract Syntax Tree
without changing the without modifying the node
classes on which it classes.
operates.

Chapter 14: Chain of Responsibility

Intent

The Chain of Responsibility pattern avoids coupling the sender of a request to its receiver by
giving more than one object a chance to handle the request. The receiving objects are
chained together, and the request is passed along the chain until an object handles it.20

Problem

Imagine an order processing system where an order request must pass through several
validation steps: authentication, authorization, data validation, and inventory check. These
checks must be performed sequentially, and if any check fails, the process should stop.
Hardcoding this sequence of checks in a single large method makes the system rigid. Adding,
removing, or reordering checks requires modifying this method, violating the Open/Closed
Principle.75

Structure and Participants

The pattern decouples the sender from the handlers by linking the handler objects into a
chain 74:
●​ Handler: Defines an interface for handling requests. It typically includes a reference to
the next handler in the chain.
●​ ConcreteHandler: Implements the handling logic. It decides whether it can process the
request. If it can, it does so; otherwise, it passes the request to the next handler in the
chain.
●​ Client: Initiates the request and sends it to the first handler in the chain.

C++ Implementation Deep Dive

This example models a system for approving purchase requests. Different manager levels
(TeamLead, Manager, Director) can approve requests up to a certain amount.

C++

#include <iostream>​
#include <string>​
#include <memory>​

class PurchaseRequest {​
public:​
double amount;​
std::string purpose;​
};​

// Handler interface​
class Approver {​
protected:​
std::shared_ptr<Approver> next_approver_;​
public:​
virtual ~Approver() = default;​
void setNext(std::shared_ptr<Approver> next) {​
next_approver_ = next;​
}​
virtual void processRequest(const PurchaseRequest& request) = 0;​
};​

// ConcreteHandler A​
class TeamLead : public Approver {​
public:​
void processRequest(const PurchaseRequest& request) override {​
if (request.amount <= 500) {​
std::cout << "Team Lead approved purchase of " << request.amount << " for " <<
request.purpose << ".\n";​
} else if (next_approver_!= nullptr) {​
next_approver_->processRequest(request);​
} else {​
std::cout << "No one can approve this request.\n";​
}​
}​
};​

// ConcreteHandler B​
class Manager : public Approver {​
public:​
void processRequest(const PurchaseRequest& request) override {​
if (request.amount > 500 && request.amount <= 5000) {​
std::cout << "Manager approved purchase of " << request.amount << " for " <<
request.purpose << ".\n";​
} else if (next_approver_!= nullptr) {​
next_approver_->processRequest(request);​
}​
}​
};​

// ConcreteHandler C​
class Director : public Approver {​
public:​
void processRequest(const PurchaseRequest& request) override {​
if (request.amount > 5000) {​
std::cout << "Director approved purchase of " << request.amount << " for " <<
request.purpose << ".\n";​
} else if (next_approver_!= nullptr) {​
next_approver_->processRequest(request);​
}​
}​
};​

int main() {​
auto lead = std::make_shared<TeamLead>();​
auto manager = std::make_shared<Manager>();​
auto director = std::make_shared<Director>();​

// Build the chain​
lead->setNext(manager);​
manager->setNext(director);​

PurchaseRequest req1{300, "New Keyboard"};​
PurchaseRequest req2{4500, "Team Server"};​
PurchaseRequest req3{12000, "Cloud Subscription"};​

lead->processRequest(req1);​
lead->processRequest(req2);​
lead->processRequest(req3);​

return 0;​
}​

The client constructs the chain of approvers: lead -> manager -> director. When a request is
sent to the lead, it either handles it or passes it on. This allows the chain to be reconfigured
dynamically without changing the handler classes themselves.77

Consequences

●​ Pros:
○​ Reduced Coupling: The sender of a request does not need to know which object will
handle it.
○​ Flexibility: The chain can be modified at runtime by adding or removing handlers.
○​ Single Responsibility: Each handler is responsible for a specific part of the
processing logic.
●​ Cons:
○​ Request Not Guaranteed to be Handled: A request might travel to the end of the
chain without being processed if no handler is configured to handle it.75
○​ Debugging: Tracing the flow of a request through a long chain can be difficult.

Chapter 15: Command

Intent
The Command pattern encapsulates a request as an object, thereby letting you parameterize
clients with different requests, queue or log requests, and support undoable operations.6

Problem

In many applications, especially those with graphical user interfaces, you need to perform
actions in response to user input. For example, a button click might save a document, a menu
item might copy text, and a keyboard shortcut might paste text. A naive implementation would
couple the UI elements (the "invokers") directly to the business logic objects (the "receivers").
This makes the UI components less reusable and the code harder to maintain, as the logic is
scattered.79

Structure and Participants

The Command pattern decouples the invoker from the receiver by introducing a command
object 78:
●​ Command: Declares an interface for executing an operation, typically a single method
like execute().
●​ ConcreteCommand: Implements the Command interface. It holds a reference to a
Receiver object and invokes one or more of its methods. It also stores any arguments
required for the receiver's methods.
●​ Invoker: (Button, MenuItem) Asks the command to carry out the request. The invoker is
not aware of the receiver or the specifics of the operation.
●​ Receiver: (Document, TextBuffer) Knows how to perform the operations associated with
carrying out a request. It contains the actual business logic.
●​ Client: Creates a ConcreteCommand object and sets its receiver.

C++ Implementation Deep Dive

This example models a simple remote control for a light.


C++

#include <iostream>​
#include <memory>​
#include <vector>​

// Receiver​
class Light {​
public:​
void turnOn() { std::cout << "The light is ON\n"; }​
void turnOff() { std::cout << "The light is OFF\n"; }​
};​

// Command interface​
class Command {​
public:​
virtual ~Command() = default;​
virtual void execute() = 0;​
virtual void undo() = 0;​
};​

// ConcreteCommand​
class LightOnCommand : public Command {​
private:​
Light& light_;​
public:​
LightOnCommand(Light& light) : light_(light) {}​
void execute() override { light_.turnOn(); }​
void undo() override { light_.turnOff(); }​
};​

class LightOffCommand : public Command {​
private:​
Light& light_;​
public:​
LightOffCommand(Light& light) : light_(light) {}​
void execute() override { light_.turnOff(); }​
void undo() override { light_.turnOn(); }​
};​

// Invoker​
class RemoteControl {​
private:​
std::shared_ptr<Command> command_;​
public:​
void setCommand(std::shared_ptr<Command> cmd) {​
command_ = cmd;​
}​
void pressButton() {​
if (command_) command_->execute();​
}​
void pressUndo() {​
if (command_) command_->undo();​
}​
};​

int main() {​
Light livingRoomLight;​
auto lightOn = std::make_shared<LightOnCommand>(livingRoomLight);​
auto lightOff = std::make_shared<LightOffCommand>(livingRoomLight);​

RemoteControl remote;​
remote.setCommand(lightOn);​
remote.pressButton(); // The light is ON​
remote.pressUndo(); // The light is OFF​

remote.setCommand(lightOff);​
remote.pressButton(); // The light is OFF​
remote.pressUndo(); // The light is ON​

return 0;​
}​

The RemoteControl (Invoker) is configured with a Command object. When pressButton() is


called, it simply calls execute() on its current command, without knowing what the command
does or who the receiver is. This example also shows how the Command pattern facilitates
undoable operations by adding an undo() method to the interface.80

Consequences
●​ Pros:
○​ Decoupling: Decouples the object that invokes the operation from the one that
knows how to perform it.
○​ Extensibility: New commands can be added easily without changing existing code.
○​ Advanced Operations: Commands are first-class objects, so they can be stored,
queued, logged, or sent over a network. This enables features like macro recording
and undo/redo.
●​ Cons:
○​ Increased Complexity: Can result in a proliferation of small command classes for
every action in the application.

Chapter 16: Interpreter

Intent

Given a language, the Interpreter pattern defines a representation for its grammar along with
an interpreter that uses this representation to interpret sentences in the language.20

Problem

Some applications require processing a simple language or expression format. For example, a
search engine might need to parse queries like "design AND (pattern OR C++)", or a financial
application might need to evaluate mathematical formulas. Implementing a parser and
interpreter for such a language can be complex. The Interpreter pattern provides a structured
way to solve this problem using an object-oriented approach.

Structure and Participants

The pattern represents the grammar as a class hierarchy, often resembling a Composite
structure 81:
●​ AbstractExpression: Declares an abstract interpret() method that is common to all
nodes in the Abstract Syntax Tree (AST).
●​ TerminalExpression: Implements the interpret() method for terminal symbols in the
grammar (e.g., numbers, variables). These are the leaves of the AST.
●​ NonterminalExpression: Implements the interpret() method for non-terminal symbols
(e.g., addition, subtraction, logical operators). These expressions are composites that
contain other AbstractExpression objects.
●​ Context: Contains information that is global to the interpreter, such as a symbol table.
●​ Client: Builds (or is given) an AST representing a sentence in the language and invokes
the interpret() method.

C++ Implementation Deep Dive

This example implements a simple interpreter for arithmetic expressions involving addition
and subtraction of integers.

C++

#include <iostream>​
#include <string>​
#include <map>​
#include <memory>​

// Context​
using Context = std::map<std::string, int>;​

// AbstractExpression​
class Expression {​
public:​
virtual ~Expression() = default;​
virtual int interpret(const Context& context) const = 0;​
};​

// TerminalExpression​
class Number : public Expression {​
private:​
int number;​
public:​
Number(int num) : number(num) {}​
int interpret(const Context& context) const override {​
return number;​
}​
};​

// NonterminalExpression​
class Plus : public Expression {​
private:​
std::shared_ptr<Expression> left;​
std::shared_ptr<Expression> right;​
public:​
Plus(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r) : left(l), right(r) {}​
int interpret(const Context& context) const override {​
return left->interpret(context) + right->interpret(context);​
}​
};​

class Minus : public Expression {​
private:​
std::shared_ptr<Expression> left;​
std::shared_ptr<Expression> right;​
public:​
Minus(std::shared_ptr<Expression> l, std::shared_ptr<Expression> r) : left(l), right(r) {}​
int interpret(const Context& context) const override {​
return left->interpret(context) - right->interpret(context);​
}​
};​

int main() {​
// Represents the expression: 5 + (10 - 2)​
auto expression = std::make_shared<Plus>(​
std::make_shared<Number>(5),​
std::make_shared<Minus>(​
std::make_shared<Number>(10),​
std::make_shared<Number>(2)​
)​
);​

Context context; // Context is empty for this simple example​
std::cout << "Result: " << expression->interpret(context) << std::endl; // Output: 13​

return 0;​
}​

The client code manually builds the AST for the expression 5 + (10 - 2). Calling interpret() on
the root node triggers a recursive evaluation of the entire tree, resulting in the final value.83

Consequences

●​ Pros:
○​ Extensibility: The grammar is easy to extend. New rules can be added by creating
new Expression subclasses.
○​ Modularity: Each grammar rule is represented by its own class, making the code
organized and maintainable.82
●​ Cons:
○​ Complexity for Large Grammars: The pattern is not practical for complex
grammars, as the number of classes can become unmanageable. For complex
languages, using a parser generator (like ANTLR or Bison) is a better approach.
○​ Limited Applicability: The pattern is best suited for simple, well-defined
grammars.82

Chapter 17: Iterator

Intent

The Iterator pattern provides a way to access the elements of an aggregate object (a
collection) sequentially without exposing its underlying representation (e.g., list, vector,
tree).20

Problem

A collection class, such as a custom list or tree, should provide a way for clients to traverse its
elements. A naive approach might be to expose the internal data structure (e.g., return a
reference to the internal std::vector). This violates encapsulation and couples the client
directly to the collection's implementation. If the internal data structure changes (e.g., from a
std::vector to a std::list), all client code that traverses the collection will break.

Structure and Participants

The Iterator pattern solves this by encapsulating the traversal logic in a separate iterator
object 85:
●​ Iterator: An interface that defines the operations for traversal, such as next(), hasNext(),
and currentItem().
●​ ConcreteIterator: Implements the Iterator interface and keeps track of the current
position in the traversal of a specific Aggregate.
●​ Aggregate: An interface that defines a factory method for creating an Iterator object
(e.g., createIterator()).
●​ ConcreteAggregate: Implements the Aggregate interface and returns an instance of a
ConcreteIterator that can traverse its elements.

C++ Implementation Deep Dive

While the C++ Standard Library provides a powerful and standardized iterator concept,
implementing the pattern from scratch is instructive. This example creates a custom
NumberCollection and a corresponding NumberIterator.

C++

#include <iostream>​
#include <vector>​
#include <stdexcept>​

// Forward declaration​
class NumberCollection;​

// Iterator interface​
class Iterator {​
public:​
virtual ~Iterator() = default;​
virtual int next() = 0;​
virtual bool hasNext() const = 0;​
};​

// Aggregate interface​
class Aggregate {​
public:​
virtual ~Aggregate() = default;​
virtual std::unique_ptr<Iterator> createIterator() = 0;​
};​

// ConcreteIterator​
class NumberIterator : public Iterator {​
private:​
NumberCollection& collection_;​
size_t index_;​
public:​
NumberIterator(NumberCollection& collection);​
int next() override;​
bool hasNext() const override;​
};​

// ConcreteAggregate​
class NumberCollection : public Aggregate {​
private:​
std::vector<int> numbers_;​
friend class NumberIterator;​
public:​
void add(int number) {​
numbers_.push_back(number);​
}​
std::unique_ptr<Iterator> createIterator() override {​
return std::make_unique<NumberIterator>(*this);​
}​
};​

// Implementation of NumberIterator methods after NumberCollection is fully defined​
NumberIterator::NumberIterator(NumberCollection& collection) : collection_(collection),
index_(0) {}​

int NumberIterator::next() {​
if (!hasNext()) {​
throw std::out_of_range("No more elements");​
}​
return collection_.numbers_[index_++];​
}​

bool NumberIterator::hasNext() const {​
return index_ < collection_.numbers_.size();​
}​


int main() {​
NumberCollection collection;​
collection.add(10);​
collection.add(20);​
collection.add(30);​

auto it = collection.createIterator();​
while (it->hasNext()) {​
std::cout << it->next() << std::endl;​
}​

return 0;​
}​

The client obtains an iterator from the NumberCollection and uses its hasNext() and next()
methods to traverse the elements. The client is completely unaware that the collection is
internally using a std::vector. The collection's implementation could be changed to a std::list or
another data structure, and only the NumberCollection and NumberIterator classes would
need to be updated; the client code would remain unchanged.87

Consequences

●​ Pros:
○​ Encapsulation: Hides the internal structure of the collection from the client.
○​ Multiple Traversals: Allows multiple iterators to traverse the same collection
independently and simultaneously.
○​ Uniform Interface: Provides a consistent interface for traversing different types of
collections.
●​ Cons:
○​ Overkill for Simple Collections: For simple collections, implementing the full
pattern might be more complex than necessary, especially when language-provided
iterators are available.

Chapter 18: Mediator

Intent

The Mediator pattern defines an object that encapsulates how a set of objects interact. It
promotes loose coupling by keeping objects from referring to each other explicitly, and it lets
you vary their interaction independently.20

Problem

In systems with many components, such as a GUI dialog with multiple widgets (buttons, text
boxes, checkboxes), the communication logic can become a tangled mess. Each widget might
need to know about and communicate with several other widgets. For example, typing in a
text box might enable a button, which, when clicked, updates a list box. This creates a
"many-to-many" web of dependencies, making the individual widget classes hard to reuse
and the overall logic difficult to understand and maintain.88

Structure and Participants

The Mediator pattern solves this by centralizing the communication logic 89:
●​ Mediator: Defines an interface for communicating with Colleague objects.
●​ ConcreteMediator: Implements the communication logic and coordinates the Colleague
objects. It knows and maintains its colleagues.
●​ Colleague: Defines an interface for communicating with the Mediator.
●​ ConcreteColleague: Each colleague class knows its Mediator object. It communicates
with its mediator whenever it would have otherwise communicated with another
colleague.

C++ Implementation Deep Dive

This example models a chat room where User objects communicate through a central
ChatMediator instead of directly with each other.

C++

#include <iostream>​
#include <string>​
#include <vector>​
#include <memory>​
#include <algorithm>​

class Colleague; // Forward declaration​

// Mediator interface​
class Mediator {​
public:​
virtual ~Mediator() = default;​
virtual void sendMessage(const std::string& message, Colleague* sender) = 0;​
};​

// Colleague base class​
class Colleague {​
protected:​
Mediator* mediator_;​
public:​
Colleague(Mediator* mediator) : mediator_(mediator) {}​
virtual ~Colleague() = default;​
};​

// ConcreteColleague​
class User : public Colleague {​
private:​
std::string name_;​
public:​
User(const std::string& name, Mediator* mediator) : Colleague(mediator), name_(name) {}​
const std::string& getName() const { return name_; }​

void send(const std::string& message) {​
std::cout << name_ << " sends: " << message << "\n";​
mediator_->sendMessage(message, this);​
}​
void receive(const std::string& message) {​
std::cout << name_ << " receives: " << message << "\n";​
}​
};​

// ConcreteMediator​
class ChatMediator : public Mediator {​
private:​
std::vector<User*> users_;​
public:​
void addUser(User* user) {​
users_.push_back(user);​
}​
void sendMessage(const std::string& message, Colleague* sender) override {​
User* senderUser = static_cast<User*>(sender);​
for (User* user : users_) {​
if (user!= senderUser) {​
user->receive(message);​
}​
}​
}​
};​

int main() {​
ChatMediator mediator;​
User alice("Alice", &mediator);​
User bob("Bob", &mediator);​
User charlie("Charlie", &mediator);​

mediator.addUser(&alice);​
mediator.addUser(&bob);​
mediator.addUser(&charlie);​

alice.send("Hi everyone!");​

return 0;​
}​

When alice.send() is called, the User object doesn't send the message to Bob and Charlie
directly. Instead, it notifies the ChatMediator. The mediator then iterates through its list of
users and forwards the message to everyone except the original sender. The User objects are
completely decoupled from each other; they only know about the Mediator.91

Consequences

●​ Pros:
○​ Reduced Coupling: Decouples colleagues, reducing dependencies and making
them easier to reuse and modify independently.89
○​ Centralized Control: The interaction logic is centralized in one place, making it
easier to understand and maintain.
○​ Simplifies Object Protocols: Replaces complex many-to-many interactions with
simpler one-to-many interactions (from each colleague to the mediator).
●​ Cons:
○​ Mediator God Object: The ConcreteMediator can become overly complex and
monolithic if it tries to manage too many colleagues and interactions.

Chapter 19: Memento

Intent

The Memento pattern, without violating encapsulation, captures and externalizes an object's
internal state so that the object can be restored to this state later.20

Problem

Sometimes it is necessary to record the internal state of an object at a particular moment and
restore it later. This is the foundation of undo/redo functionality, database transactions, and
"save game" features.93 The challenge is to get the state out of the object without exposing its
internal implementation details, which would violate encapsulation.

Structure and Participants

The Memento pattern solves this with three key roles 92:
●​ Originator: The object whose state needs to be saved. It creates a Memento containing a
snapshot of its current state and uses a Memento to restore its state.
●​ Memento: A passive object that stores the internal state of the Originator. It should
protect against access by objects other than the originator. In C++, this can be achieved
using the friend keyword.
●​ Caretaker: Is responsible for the memento's safekeeping but never operates on or
examines its contents. It requests a memento from the originator to save a state and
passes a memento back to the originator to restore a state.

C++ Implementation Deep Dive

This example implements a simple text editor that can save and restore its content.

C++

#include <iostream>​
#include <string>​
#include <vector>​
#include <memory>​

// Memento​
class EditorMemento {​
private:​
std::string content_;​
// Make Originator a friend to allow it to access the private state​
friend class TextEditor; ​
EditorMemento(const std::string& content) : content_(content) {}​
};​

// Originator​
class TextEditor {​
private:​
std::string content_;​
public:​
void type(const std::string& text) {​
content_ += text;​
}​
const std::string& getContent() const {​
return content_;​
}​

std::shared_ptr<EditorMemento> save() {​
return std::make_shared<EditorMemento>(content_);​
}​

void restore(std::shared_ptr<EditorMemento> memento) {​
if (memento) {​
content_ = memento->content_;​
}​
}​
};​

// Caretaker​
class History {​
private:​
std::vector<std::shared_ptr<EditorMemento>> mementos_;​
std::shared_ptr<TextEditor> editor_;​
public:​
History(std::shared_ptr<TextEditor> editor) : editor_(editor) {}​

void backup() {​
mementos_.push_back(editor_->save());​
}​

void undo() {​
if (mementos_.empty()) return;​

auto memento = mementos_.back();​
mementos_.pop_back();​

editor_->restore(memento);​
}​
};​

int main() {​
auto editor = std::make_shared<TextEditor>();​
History history(editor);​

history.backup();​
editor->type("Hello, ");​
std::cout << editor->getContent() << std::endl;​

history.backup();​
editor->type("World!");​
std::cout << editor->getContent() << std::endl;​

history.undo();​
std::cout << "After undo: " << editor->getContent() << std::endl;​

return 0;​
}​

The EditorMemento's constructor and state are private. By declaring TextEditor as a friend,
only the TextEditor (the Originator) can create mementos and access their internal state to
restore itself. The History (Caretaker) holds a list of EditorMemento objects but cannot access
their private content_ member, thus preserving the TextEditor's encapsulation.95

Consequences

●​ Pros:
○​ Preserves Encapsulation: The state is saved without exposing the internal structure
of the Originator.
○​ Simplifies Originator: The Originator doesn't need to manage its history; this logic is
handled by the Caretaker.
○​ Enables Undo/Redo: Provides a clean mechanism for implementing state restoration
features.93
●​ Cons:
○​ Memory Consumption: Storing many mementos can be memory-intensive,
especially if the Originator's state is large.
○​ Language Dependency: The ability to restrict access to the memento's state may
depend on language features (like friend in C++).

Chapter 20: Observer

Intent

The Observer pattern defines a one-to-many dependency between objects so that when one
object (the subject) changes state, all its dependents (observers) are notified and updated
automatically.6

Problem

In many systems, a change in one object requires other objects to be updated. For example, in
a spreadsheet, when the value of one cell changes, all other cells that use its value in their
formulas must be recalculated. A naive implementation might hardcode the cell
dependencies, but this creates tight coupling. The cell that changes (the subject) would need
explicit knowledge of all the cells that depend on it (the observers), making the system
difficult to maintain and extend.

Structure and Participants

The Observer pattern decouples the subject from its observers 97:
●​ Subject: Knows its observers and provides an interface for attaching and detaching
Observer objects.
●​ Observer: Defines an updating interface for objects that should be notified of changes in
a subject.
●​ ConcreteSubject: Stores state of interest to ConcreteObserver objects and sends a
notification to its observers when its state changes.
●​ ConcreteObserver: Maintains a reference to a ConcreteSubject object and implements
the Observer updating interface to keep its state consistent with the subject's.
C++ Implementation Deep Dive

This example models a weather station (WeatherStation) that notifies various displays
(CurrentConditionsDisplay) when weather data changes.

C++

#include <iostream>​
#include <vector>​
#include <string>​
#include <algorithm>​
#include <memory>​

// Observer interface​
class IObserver {​
public:​
virtual ~IObserver() = default;​
virtual void update(float temp, float humidity) = 0;​
};​

// Subject interface​
class ISubject {​
public:​
virtual ~ISubject() = default;​
virtual void registerObserver(IObserver* o) = 0;​
virtual void removeObserver(IObserver* o) = 0;​
virtual void notifyObservers() = 0;​
};​

// ConcreteSubject​
class WeatherStation : public ISubject {​
private:​
std::vector<IObserver*> observers;​
float temperature;​
float humidity;​

public:​
void registerObserver(IObserver* o) override {​
observers.push_back(o);​
}​
void removeObserver(IObserver* o) override {​
observers.erase(std::remove(observers.begin(), observers.end(), o), observers.end());​
}​
void notifyObservers() override {​
for (IObserver* observer : observers) {​
observer->update(temperature, humidity);​
}​
}​
void setMeasurements(float temp, float hum) {​
this->temperature = temp;​
this->humidity = hum;​
notifyObservers();​
}​
};​

// ConcreteObserver​
class CurrentConditionsDisplay : public IObserver {​
public:​
void update(float temp, float humidity) override {​
std::cout << "Current conditions: " << temp << "F degrees and " << humidity << "% humidity\n";​
}​
};​

int main() {​
WeatherStation weatherStation;​
CurrentConditionsDisplay display1;​

weatherStation.registerObserver(&display1);​
weatherStation.setMeasurements(80, 65);​
weatherStation.setMeasurements(82, 70);​

return 0;​
}​

The WeatherStation (Subject) maintains a list of IObserver pointers. When setMeasurements is


called, it updates its state and then calls notifyObservers, which iterates through the list and
calls update on each registered observer. The WeatherStation has no knowledge of the
concrete observer types (like CurrentConditionsDisplay), achieving loose coupling.99

A common issue in C++ implementations is the "dangling observer" problem: if an observer is


destroyed before it is detached from the subject, the subject will be left with a dangling
pointer. A modern C++ solution is for the subject to store a
std::vector<std::weak_ptr<IObserver>>. Observers are owned by std::shared_ptrs elsewhere.
Before notifying, the subject checks if each weak_ptr can be locked into a shared_ptr,
ensuring the observer still exists.100

Consequences

●​ Pros:
○​ Loose Coupling: The subject and observers are loosely coupled. The subject only
knows that its observers implement the IObserver interface.99
○​ Dynamic Relationships: Observers can be added or removed at any time.
○​ Broadcast Communication: Supports a one-to-many notification model.
●​ Cons:
○​ Unexpected Updates: Observers are notified of all changes, even if they are not
interested in every change. This can lead to inefficient "update storms".99
○​ Update Order: The order in which observers are notified is not guaranteed.

Chapter 21: State

Intent

The State pattern allows an object to alter its behavior when its internal state changes. The
object will appear to change its class.20

Problem

An object's behavior often depends on its current state. For example, a Document object
might behave differently depending on whether it is in a Draft, Moderation, or Published state.
A common but poor way to implement this is with a large switch or if-else statement inside the
object's methods, checking the current state and executing the appropriate behavior. This
approach is problematic because it violates the Single Responsibility and Open/Closed
principles. The method becomes bloated with state-specific logic, and adding a new state
requires modifying this large conditional block.

Structure and Participants

The State pattern solves this by encapsulating the state-specific behaviors into separate
classes 101:
●​ Context: (Document) The object whose behavior changes with its state. It maintains an
instance of a ConcreteState subclass that defines the current state.
●​ State: An interface that encapsulates the behavior associated with a particular state of
the Context.
●​ ConcreteState: (DraftState, PublishedState) Each subclass implements a behavior
associated with a state of the Context. They can also be responsible for transitioning the
Context to a new state.

C++ Implementation Deep Dive

This example models a simple traffic light system. The TrafficLight (Context) delegates its
change() method to its current State object.

C++

#include <iostream>​
#include <memory>​
#include <typeinfo>​

class TrafficLight; // Forward declaration​

// State interface​
class LightState {​
public:​
virtual ~LightState() = default;​
virtual void change(TrafficLight& light) = 0;​
virtual void reportState() const = 0;​
};​

// Context​
class TrafficLight {​
private:​
std::unique_ptr<LightState> state_;​
public:​
TrafficLight(std::unique_ptr<LightState> initial_state);​
void transitionTo(std::unique_ptr<LightState> state) {​
state_ = std::move(state);​
std::cout << "Traffic light is now ";​
state_->reportState();​
std::cout << ".\n";​
}​
void requestChange() {​
state_->change(*this);​
}​
};​

// ConcreteStates​
class RedLight : public LightState {​
public:​
void change(TrafficLight& light) override;​
void reportState() const override { std::cout << "RED"; }​
};​

class GreenLight : public LightState {​
public:​
void change(TrafficLight& light) override;​
void reportState() const override { std::cout << "GREEN"; }​
};​

class YellowLight : public LightState {​
public:​
void change(TrafficLight& light) override;​
void reportState() const override { std::cout << "YELLOW"; }​
};​

// Implement state transitions after all classes are defined​
void RedLight::change(TrafficLight& light) {​
light.transitionTo(std::make_unique<GreenLight>());​
}​
void GreenLight::change(TrafficLight& light) {​
light.transitionTo(std::make_unique<YellowLight>());​
}​
void YellowLight::change(TrafficLight& light) {​
light.transitionTo(std::make_unique<RedLight>());​
}​

// Context constructor​
TrafficLight::TrafficLight(std::unique_ptr<LightState> initial_state) {​
transitionTo(std::move(initial_state));​
}​


int main() {​
TrafficLight light(std::make_unique<RedLight>());​

light.requestChange(); // -> Green​
light.requestChange(); // -> Yellow​
light.requestChange(); // -> Red​

return 0;​
}​

The TrafficLight's behavior for requestChange() is entirely determined by its current state_.
The logic for state transitions is encapsulated within the ConcreteState classes themselves
(e.g., RedLight::change transitions the context to GreenLight). This eliminates the need for
large conditional statements in the TrafficLight class and makes it easy to add new states
without modifying existing code.

Relations with Other Patterns

The State and Strategy patterns are structurally very similar, both relying on composition to
delegate behavior to another object. The key difference lies in their intent. Strategy is typically
used to provide different algorithms for a single task, and the client often chooses the
strategy. State is used to manage an object's behavior as its internal state changes, and the
state transitions are often managed by the context or the state objects themselves.103
Chapter 22: Strategy

Intent

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them
interchangeable. Strategy lets the algorithm vary independently from clients that use it.6

Problem

An application may need to perform a specific task in different ways. For example, a
navigation app might need to calculate a route for a car, a bicycle, or for walking. A data
compression utility might need to use different compression algorithms (ZIP, RAR, 7z).
Hardcoding these algorithms into the main class using a large switch statement makes the
class difficult to maintain. Adding a new algorithm requires modifying the class, violating the
Open/Closed Principle.

Structure and Participants

The Strategy pattern extracts these algorithms into separate classes called "strategies".103
●​ Strategy: An interface common to all supported algorithms.
●​ ConcreteStrategy: Implements a specific algorithm using the Strategy interface.
●​ Context: Is configured with a ConcreteStrategy object and maintains a reference to it. It
communicates with the strategy object via the Strategy interface. The Context is not
aware of the concrete type of strategy it is using.

C++ Implementation Deep Dive

This example shows a Sorter class that can be configured with different sorting algorithms at
runtime.

C++

#include <iostream>​
#include <vector>​
#include <algorithm>​
#include <memory>​

// Strategy interface​
class SortStrategy {​
public:​
virtual ~SortStrategy() = default;​
virtual void sort(std::vector<int>& data) const = 0;​
};​

// ConcreteStrategy A​
class BubbleSortStrategy : public SortStrategy {​
public:​
void sort(std::vector<int>& data) const override {​
std::cout << "Sorting using Bubble Sort.\n";​
// Simplified bubble sort logic​
for (size_t i = 0; i < data.size(); ++i) {​
for (size_t j = 0; j < data.size() - 1; ++j) {​
if (data[j] > data[j+1]) {​
std::swap(data[j], data[j+1]);​
}​
}​
}​
}​
};​

// ConcreteStrategy B​
class QuickSortStrategy : public SortStrategy {​
public:​
void sort(std::vector<int>& data) const override {​
std::cout << "Sorting using Quick Sort.\n";​
std::sort(data.begin(), data.end()); // Using std::sort for simplicity​
}​
};​

// Context​
class Sorter {​
private:​
std::unique_ptr<SortStrategy> strategy_;​
public:​
Sorter(std::unique_ptr<SortStrategy> strategy) : strategy_(std::move(strategy)) {}​

void set_strategy(std::unique_ptr<SortStrategy> strategy) {​
strategy_ = std::move(strategy);​
}​

void performSort(std::vector<int>& data) {​
if (strategy_) {​
strategy_->sort(data);​
}​
}​
};​

int main() {​
std::vector<int> data = {5, 1, 4, 2, 8};​

Sorter sorter(std::make_unique<BubbleSortStrategy>());​
sorter.performSort(data);​

data = {5, 1, 4, 2, 8}; // Reset data​
sorter.set_strategy(std::make_unique<QuickSortStrategy>());​
sorter.performSort(data);​

return 0;​
}​

The Sorter (Context) is initialized with a sorting strategy. The client can change this strategy at
any time using set_strategy. The Sorter's performSort method simply delegates the work to
the current strategy object. This allows the sorting algorithm to be selected and changed
dynamically.

A modern C++ approach for simpler, stateless strategies is to use std::function, which can
wrap any callable object, including free functions, lambdas, or functors, avoiding the need for
a full class hierarchy.100
Consequences

●​ Pros:
○​ Interchangeable Algorithms: You can swap algorithms at runtime.
○​ Isolation: The implementation details of an algorithm are isolated from the client
code that uses it.
○​ Composition over Inheritance: Avoids coupling the context to specific algorithms
through inheritance.
○​ Open/Closed Principle: New strategies can be introduced without modifying the
context.
●​ Cons:
○​ Increased Objects: The pattern increases the number of objects in the application.
○​ Client Awareness: The client must be aware of the different strategies to select the
appropriate one.

Chapter 23: Template Method

Intent

The Template Method pattern defines the skeleton of an algorithm in a base class but lets
subclasses override specific steps of the algorithm without changing its structure.20

Problem

Suppose you are developing a data processing framework. The overall process for handling
different data files (e.g., CSV, JSON) is the same: open the file, extract the data, process the
data, and close the file. While the overall structure of this algorithm is constant, the specific
implementation of opening, extracting, and processing the data will differ for each file format.
Placing the entire algorithm in each subclass would lead to significant code duplication for
the common steps.
Structure and Participants

The Template Method pattern solves this by defining the algorithm's structure in a base
class.104
●​ AbstractClass: Defines the "template method," which calls a series of primitive
operations. It also defines the abstract primitive operations that concrete subclasses
must implement. It can also define "hooks," which are optional steps with a default (often
empty) implementation that subclasses can override.
●​ ConcreteClass: Implements the primitive operations to carry out the subclass-specific
steps of the algorithm.

C++ Implementation Deep Dive

This example implements a simple beverage-making process. The overall algorithm for making
tea and coffee is similar (boil water, brew, pour in cup, add condiments), but the specific
brewing and condiment steps differ.

C++

#include <iostream>​

// AbstractClass​
class CaffeineBeverage {​
public:​
// The Template Method​
void prepareRecipe() final {​
boilWater();​
brew();​
pourInCup();​
if (customerWantsCondiments()) {​
addCondiments();​
}​
}​

protected:​
// Primitive operations to be implemented by subclasses​
virtual void brew() = 0;​
virtual void addCondiments() = 0;​

// Concrete operations shared by all subclasses​
void boilWater() {​
std::cout << "Boiling water\n";​
}​
void pourInCup() {​
std::cout << "Pouring into cup\n";​
}​

// A "hook" method with a default implementation​
virtual bool customerWantsCondiments() {​
return true;​
}​
};​

// ConcreteClass A​
class Tea : public CaffeineBeverage {​
protected:​
void brew() override {​
std::cout << "Steeping the tea\n";​
}​
void addCondiments() override {​
std::cout << "Adding lemon\n";​
}​
};​

// ConcreteClass B​
class Coffee : public CaffeineBeverage {​
protected:​
void brew() override {​
std::cout << "Dripping coffee through filter\n";​
}​
void addCondiments() override {​
std::cout << "Adding sugar and milk\n";​
}​
// Overriding the hook​
bool customerWantsCondiments() override {​
return false;​
}​
};​

int main() {​
Tea myTea;​
std::cout << "Making tea...\n";​
myTea.prepareRecipe();​

std::cout << "\nMaking coffee...\n";​
Coffee myCoffee;​
myCoffee.prepareRecipe();​

return 0;​
}​

The prepareRecipe() method is the template method. It is marked final to prevent subclasses
from overriding the algorithm's structure. It calls the abstract brew() and addCondiments()
methods, which are implemented by Tea and Coffee to provide their specific behaviors. The
customerWantsCondiments() method is a hook that allows subclasses to optionally alter the
algorithm's flow.

Relations with Other Patterns

Template Method is based on inheritance, whereas Strategy is based on composition. A


Template Method lets you alter parts of an algorithm by subclassing, while Strategy lets you
replace the entire algorithm with a different object.104

Chapter 24: Visitor

Intent

The Visitor pattern represents an operation to be performed on the elements of an object


structure. It lets you define a new operation without changing the classes of the elements on
which it operates.20
Problem

Consider a complex object structure, such as an Abstract Syntax Tree (AST) representing
code, or a document composed of different elements like paragraphs and images. You may
need to perform various unrelated operations on this structure, such as printing,
type-checking, or exporting to XML. Adding a new virtual function for each new operation to
the base Node class is not feasible. It would clutter the interface and violate the Open/Closed
Principle, as every new operation would require modifying all node classes.106

Structure and Participants

The Visitor pattern solves this by separating the operations from the object structure 105:
●​ Visitor: An interface that declares a visit() method for each class of ConcreteElement in
the object structure. The method signature, e.g., visit(ConcreteElementA*), allows the
visitor to know the concrete type of the element it is visiting.
●​ ConcreteVisitor: Implements the Visitor interface, providing the specific algorithm for
each ConcreteElement.
●​ Element: An interface that declares an accept() method that takes a visitor as an
argument.
●​ ConcreteElement: Implements the accept() method. Its implementation is simple: it calls
the visit() method on the visitor, passing itself (this) as the argument.
●​ ObjectStructure: A collection of Element objects (e.g., a Composite). It provides a way
to iterate over its elements and allow a visitor to visit them.

This technique is known as "double dispatch." The first dispatch is the call to accept(), which
resolves to the concrete element's type. The second dispatch is the call to visit(), which
resolves to the concrete visitor's type and the concrete element's type (via method
overloading).

C++ Implementation Deep Dive

This example shows how to apply different operations (visitors) to a hierarchy of geometric
shapes.

C++

#include <iostream>​
#include <vector>​
#include <memory>​

// Forward declare elements for the Visitor interface​
class Circle;​
class Square;​

// Visitor interface​
class ShapeVisitor {​
public:​
virtual ~ShapeVisitor() = default;​
virtual void visit(const Circle& circle) = 0;​
virtual void visit(const Square& square) = 0;​
};​

// Element interface​
class Shape {​
public:​
virtual ~Shape() = default;​
virtual void accept(ShapeVisitor& visitor) const = 0;​
};​

// ConcreteElements​
class Circle : public Shape {​
public:​
void accept(ShapeVisitor& visitor) const override {​
visitor.visit(*this);​
}​
};​

class Square : public Shape {​
public:​
void accept(ShapeVisitor& visitor) const override {​
visitor.visit(*this);​
}​
};​

// ConcreteVisitor A​
class DrawVisitor : public ShapeVisitor {​
public:​
void visit(const Circle& circle) override {​
std::cout << "Drawing a circle.\n";​
}​
void visit(const Square& square) override {​
std::cout << "Drawing a square.\n";​
}​
};​

// ConcreteVisitor B​
class XMLExportVisitor : public ShapeVisitor {​
public:​
void visit(const Circle& circle) override {​
std::cout << "<circle/>\n";​
}​
void visit(const Square& square) override {​
std::cout << "<square/>\n";​
}​
};​

int main() {​
std::vector<std::unique_ptr<Shape>> shapes;​
shapes.push_back(std::make_unique<Circle>());​
shapes.push_back(std::make_unique<Square>());​

DrawVisitor drawer;​
XMLExportVisitor exporter;​

std::cout << "Drawing all shapes:\n";​
for (const auto& shape : shapes) {​
shape->accept(drawer);​
}​

std::cout << "\nExporting all shapes to XML:\n";​
for (const auto& shape : shapes) {​
shape->accept(exporter);​
}​

return 0;​
}​
The client creates a collection of Shape objects. To draw them, it creates a DrawVisitor and
passes it to each shape's accept method. To export them, it creates an XMLExportVisitor and
does the same. New operations can be added by creating new visitor classes without ever
touching the Shape, Circle, or Square classes.107

Consequences

●​ Pros:
○​ Open/Closed Principle: New operations can be added easily by creating new visitor
classes.
○​ Separation of Concerns: Related operations are grouped together in a single visitor
class, rather than being scattered across the element hierarchy.
○​ Visits Complex Structures: Visitors can accumulate state as they traverse a
complex object structure (like a Composite).
●​ Cons:
○​ Breaks Encapsulation: The visitor often needs access to the internal state of the
elements, which may require making their members public.
○​ Difficult to Add New Elements: Adding a new ConcreteElement class is difficult, as
it requires updating the Visitor interface and all ConcreteVisitor classes to add a new
visit method. This pattern is best used when the element hierarchy is stable.

Part IV: Patterns in the Modern C++ Landscape

The Gang of Four patterns were cataloged in an era dominated by C++98 and Smalltalk. While
their underlying principles remain timeless, the advent of modern C++ (C++11 and beyond)
has profoundly changed how these patterns are implemented and, in some cases, whether
they are needed at all.1 Modern C++ features like smart pointers, move semantics, lambda
expressions, and advanced template metaprogramming offer more elegant, safer, and often
more performant ways to solve the same problems.

Modern C++ Idioms and Patterns


Before revisiting the GoF patterns, it is crucial to understand some core modern C++ idioms
that have become patterns in their own right.
●​ RAII (Resource Acquisition Is Initialization): This is arguably the most fundamental
principle of modern C++. It states that a resource's lifetime (like memory, file handles, or
mutex locks) should be tied to the lifetime of an object. The resource is acquired in the
object's constructor and released in its destructor. This guarantees that resources are
automatically cleaned up when the object goes out of scope, preventing leaks even in the
presence of exceptions. std::unique_ptr, std::shared_ptr, and std::lock_guard are all
standard library implementations of the RAII pattern.109
●​ Type Erasure: This technique allows for storing objects of different concrete types in a
uniform way, without requiring them to share a common base class. It "erases" the
specific type information, interacting with the stored object through a common, abstract
interface.

Works cited

1.​ en.wikipedia.org, accessed on August 21, 2025,


https://en.wikipedia.org/wiki/Design_Patterns
2.​ Design Patterns: Elements of Reusable Object-Oriented Software - Goodreads,
accessed on August 21, 2025,
https://www.goodreads.com/book/show/85009.Design_Patterns
3.​ Gang of 4 Design Patterns Explained: Creational, Structural, and ..., accessed on
August 21, 2025,
https://www.digitalocean.com/community/tutorials/gangs-of-four-gof-design-pa
tterns
4.​ Gang of Four Design Patterns - Spring Framework Guru, accessed on August 21,
2025, https://springframework.guru/gang-of-four-design-patterns/
5.​ en.wikipedia.org, accessed on August 21, 2025,
https://en.wikipedia.org/wiki/Software_design_pattern
6.​ Software Design Patterns : An Introduction - ServiceNow Community, accessed
on August 21, 2025,
https://www.servicenow.com/community/developer-blog/software-design-patter
ns-an-introduction/ba-p/3139896
7.​ Software Design Patterns 101: A Beginner's Guide | by Digicore | Medium,
accessed on August 21, 2025,
https://medium.com/@digicore/software-design-patterns-101-a-beginners-guide
-c6860ef8bb63
8.​ Software Design Patterns Tutorial - GeeksforGeeks, accessed on August 21,
2025, https://www.geeksforgeeks.org/system-design/software-design-patterns/
9.​ Is design patterns not much used in C++ coding? I ask this cause most of the
resources I find on this topic especially video tutorial are in Java : r/cpp - Reddit,
accessed on August 21, 2025,
https://www.reddit.com/r/cpp/comments/1b4imd1/is_design_patterns_not_much_
used_in_c_coding_i/
10.​Your opinion on design patterns : r/cpp - Reddit, accessed on August 21, 2025,
https://www.reddit.com/r/cpp/comments/10lrlqj/your_opinion_on_design_patterns
/
11.​ The 23 Gang of Three Design Patterns - Infinite Script, accessed on August 21,
2025,
https://www.infinitescript.com/2014/10/the-23-gang-of-three-design-patterns/
12.​All Design Patterns in One Easy Reference Guide | Medium - Maxim Gorin,
accessed on August 21, 2025,
https://maxim-gorin.medium.com/finished-all-23-patterns-lets-wrap-it-up-toget
her-f22c736ba5ad
13.​What is a Software Design Pattern? (+7 Most Popular Patterns) - Net Solutions,
accessed on August 21, 2025,
https://www.netsolutions.com/insights/software-design-pattern/
14.​The 23 Design Patterns you must know | Gabriel's Blog, accessed on August 21,
2025,
https://gabrielfs7.github.io/software-architecture/design-patterns/2019/09/12/the-
23-design-patterns-you-must-know/
15.​fx-biocoder/solid-in-cpp: A quick reference guide to ... - GitHub, accessed on
August 21, 2025, https://github.com/fx-biocoder/solid-in-cpp
16.​SOLID Principles with Real Life Examples - GeeksforGeeks, accessed on August
21, 2025,
https://www.geeksforgeeks.org/system-design/solid-principle-in-programming-u
nderstand-with-real-life-examples/
17.​How to write SOLID C++ - Dimitris Platis, accessed on August 21, 2025,
https://platis.solutions/blog/2020/06/22/how-to-write-solid-cpp/
18.​SOLID principles: implementation and examples in C++ | by Oleksandra Shershen
- Medium, accessed on August 21, 2025,
https://medium.com/@oleksandra_shershen/solid-principles-implementation-and
-examples-in-c-99f0d7e3e868
19.​SOLID - Wikipedia, accessed on August 21, 2025,
https://en.wikipedia.org/wiki/SOLID
20.​The 23 Gang of Four Design Patterns | by Richard Bui - Medium, accessed on
August 21, 2025,
https://buihuycuong.medium.com/the-23-gang-of-four-design-patterns-974ae8
d1a957
21.​Singleton Pattern in c++ - Medium, accessed on August 21, 2025,
https://medium.com/@kamresh485/singleton-pattern-in-c-71c4bf61a3df
22.​Singleton Pattern | C++ Design Patterns - GeeksforGeeks, accessed on August 21,
2025,
https://www.geeksforgeeks.org/system-design/singleton-pattern-c-design-patte
rns/
23.​Implementation of Singleton Class in C++ - GeeksforGeeks, accessed on August
21, 2025,
https://www.geeksforgeeks.org/cpp/implementation-of-singleton-class-in-cpp/
24.​Singleton in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/singleton/cpp/example
25.​Implementation of C++ Singleton Class - Stack Overflow, accessed on August 21,
2025,
https://stackoverflow.com/questions/77938306/implementation-of-c-singleton-cl
ass
26.​Problems with Singleton Pattern - c++ - Stack Overflow, accessed on August 21,
2025,
https://stackoverflow.com/questions/1392315/problems-with-singleton-pattern
27.​Factory Method in C++ / Design Patterns - Refactoring.Guru, accessed on August
21, 2025, https://refactoring.guru/design-patterns/factory-method/cpp/example
28.​Factory method Design Pattern - GeeksforGeeks, accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/factory-method-for-designing-p
attern/
29.​Design Patterns: Factory Method - 2020 - BogoToBogo, accessed on August 21,
2025, https://www.bogotobogo.com/DesignPatterns/factorymethod.php
30.​Factory Method Pattern | C++ Design Patterns - GeeksforGeeks, accessed on
August 21, 2025,
https://www.geeksforgeeks.org/system-design/factory-method-pattern-c-desig
n-patterns/
31.​Abstract Factory in C++ / Design Patterns - Refactoring.Guru, accessed on
August 21, 2025,
https://refactoring.guru/design-patterns/abstract-factory/cpp/example
32.​Abstract Factory Pattern - GeeksforGeeks, accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/abstract-factory-pattern/
33.​Abstract Factory Design Pattern in C++: Before and after - SourceMaking,
accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/abstract_factory/cpp/before-after
34.​Builder - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/builder
35.​Builder Pattern in C++ - Medium, accessed on August 21, 2025,
https://medium.com/@kamresh485/builder-pattern-in-c-6ffaeb44afe7
36.​Builder pattern: A pragmatic approach - Dimitris Platis, accessed on August 21,
2025, https://platis.solutions/blog/2023/01/03/builder-pattern-cpp/
37.​Builder in C++ / Design Patterns - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/builder/cpp/example
38.​Builder Design Pattern - GeeksforGeeks, accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/builder-design-pattern/
39.​Builder Pattern | C++ Design Patterns - GeeksforGeeks, accessed on August 21,
2025,
https://www.geeksforgeeks.org/system-design/builder-pattern-c-design-pattern
s/
40.​Prototype Pattern in c++ - Medium, accessed on August 21, 2025,
https://medium.com/@kamresh485/prototype-pattern-in-c-068824ce0306
41.​Prototype Design Pattern in Modern C++ - DZone, accessed on August 21, 2025,
https://dzone.com/articles/prototype-design-pattern-in-modern-c
42.​Prototype in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/prototype/cpp/example
43.​Design Patterns: Prototype Pattern - 2020 - BogoToBogo, accessed on August 21,
2025, https://www.bogotobogo.com/DesignPatterns/prototype.php
44.​Adapter in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/adapter/cpp/example
45.​Adapter - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/adapter
46.​Adapter Pattern | C++ Design Patterns - GeeksforGeeks, accessed on August 21,
2025,
https://www.geeksforgeeks.org/system-design/adapter-pattern-c-design-patter
ns/
47.​Adapter Design Pattern - GeeksforGeeks, accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/adapter-pattern/
48.​Adapter Design Pattern in Modern C++ | by Vishal Chovatiya | Dev Genius,
accessed on August 21, 2025,
https://blog.devgenius.io/adapter-design-pattern-in-modern-c-32d92d56c4bf
49.​Bridge in C++ / Design Patterns - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/bridge/cpp/example
50.​Bridge Design Pattern in Modern C++ - DEV Community, accessed on August 21,
2025, https://dev.to/visheshpatel/bridge-design-pattern-in-modern-c-57m4
51.​Bridge - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/bridge
52.​The Bridge Pattern in C++ - Medium, accessed on August 21, 2025,
https://medium.com/@kamresh485/the-bridge-pattern-in-c-7ad790e0ce5f
53.​Bridge Design Pattern, accessed on August 21, 2025,
https://www2.seas.gwu.edu/~bell/csci210/bridge_design_pattern_example.htm
54.​Composite in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/composite/cpp/example
55.​Design Patterns: Composite Pattern - 2020 - BogoToBogo, accessed on August
21, 2025, https://www.bogotobogo.com/DesignPatterns/composite.php
56.​Composite Design Pattern in C++ - GeeksforGeeks, accessed on August 21,
2025, https://www.geeksforgeeks.org/cpp/composite-pattern-cpp/
57.​Composite Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/composite/cpp/1
58.​Decorator in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/decorator/cpp/example
59.​Introduction to Decorator Pattern in C++ | Design Patterns - GeeksforGeeks,
accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/introduction-to-decorator-patter
n-in-c-design-patterns/
60.​Understanding the Decorator Pattern in C++ | by Amresh Kumar - Medium,
accessed on August 21, 2025,
https://medium.com/@kamresh485/understanding-the-decorator-pattern-in-c-c
b7abd37ade6
61.​The Decorator Pattern: From Basic to Advanced Concepts in C++ - John Farrier,
accessed on August 21, 2025,
https://johnfarrier.com/the-decorator-pattern-from-basics-to-advanced-concep
ts-in-c/
62.​Facade in C++ / Design Patterns - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/facade/cpp/example
63.​Design Patterns: Facade Pattern - 2020 - BogoToBogo, accessed on August 21,
2025, https://www.bogotobogo.com/DesignPatterns/facade.php
64.​Facade - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/facade
65.​Facade Method - C++ Design Patterns - GeeksforGeeks, accessed on August 21,
2025,
https://www.geeksforgeeks.org/system-design/facade-method-c-design-pattern
s/
66.​The Facade Pattern Explained - Codefinity, accessed on August 21, 2025,
https://codefinity.com/blog/The-Facade-Pattern-Explained
67.​Flyweight in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/flyweight/cpp/example
68.​C++ Flyweight Design Pattern : Comprehensive Deep Dive | by Chetanp Verma -
Medium, accessed on August 21, 2025,
https://medium.com/@chetanp.verma98/c-flyweight-design-pattern-comprehen
sive-deep-dive-4ca3cf579b53
69.​Proxy - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/proxy
70.​Proxy Design Pattern - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/proxy
71.​Proxy Pattern | C++ Design Patterns - GeeksforGeeks, accessed on August 21,
2025,
https://www.geeksforgeeks.org/system-design/proxy-pattern-c-design-patterns/
72.​Proxy in C++ / Design Patterns - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/proxy/cpp/example
73.​Proxy Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/proxy/cpp/3
74.​Chain of Responsibility in C++ / Design Patterns - Refactoring.Guru, accessed on
August 21, 2025,
https://refactoring.guru/design-patterns/chain-of-responsibility/cpp/example
75.​Chain of Responsibility - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/chain-of-responsibility
76.​Chain of Responsibility in C++ - Medium, accessed on August 21, 2025,
https://medium.com/@antwang/chain-of-responsibility-in-c-6d422ff6e3fa
77.​Chain of Responsibility Design Pattern in Modern C++ - DEV Community,
accessed on August 21, 2025,
https://dev.to/visheshpatel/chain-of-responsibility-design-pattern-in-modern-c-4
g5n
78.​Command in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/command/cpp/example
79.​Command - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/command
80.​Command · Design Patterns Revisited - Game Programming Patterns, accessed
on August 21, 2025, https://gameprogrammingpatterns.com/command.html
81.​Interpreter Design Pattern in C++ - GeeksforGeeks, accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/command-design-pattern-in-java
/
82.​Interpreter - Design Patterns in 5 minutes - YouTube, accessed on August 21,
2025, https://www.youtube.com/watch?v=Ia83UqTNApY
83.​Interpreter Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/interpreter/cpp/1
84.​Iterator in C++ / Design Patterns - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/iterator/cpp/example
85.​Iterator Design Pattern - GeeksforGeeks, accessed on August 21, 2025,
https://www.geeksforgeeks.org/system-design/iterator-pattern/
86.​C++ Iterator Design Pattern : Comprehensive Deep Dive | by Chetanp Verma -
Medium, accessed on August 21, 2025,
https://medium.com/@chetanp.verma98/c-iterator-design-pattern-comprehensiv
e-deep-dive-96d28790a703
87.​Iterator Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/iterator/cpp/1
88.​Mediator - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/mediator
89.​C++ Mediator Design Pattern : Comprehensive Deep Dive | by Chetanp Verma -
Medium, accessed on August 21, 2025,
https://medium.com/@chetanp.verma98/c-mediator-design-pattern-comprehens
ive-deep-dive-75072d052f32
90.​Mediator in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/mediator/cpp/example
91.​Mediator Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/mediator/cpp/1
92.​Memento in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/memento/cpp/example
93.​Memento Design Pattern | C++ Design Patterns - GeeksforGeeks, accessed on
August 21, 2025,
https://www.geeksforgeeks.org/system-design/memento-design-pattern-c-desi
gn-patterns/
94.​Memento Software Pattern C++ Examples, accessed on August 21, 2025,
https://softwarepatterns.com/cpp/memento-software-pattern-cpp-example
95.​Memento Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/memento/cpp/1
96.​Observer in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/observer/cpp/example
97.​Observer Pattern in C++. This is the 4th pattern of my 8 part… | by Lokesh Bihani -
Medium, accessed on August 21, 2025,
https://medium.com/@lokeshbihani99/observer-pattern-in-c-366a1e9226f6
98.​Observer Design Pattern in C++ - SourceMaking, accessed on August 21, 2025,
https://sourcemaking.com/design_patterns/observer/cpp/3
99.​Observer Pattern | C++ Design Patterns - GeeksforGeeks, accessed on August
21, 2025,
https://www.geeksforgeeks.org/system-design/observer-pattern-c-design-patter
ns/
100.​ An Authoritative Guide to C++ Design Patterns | by offline-pixel | Aug, 2025 |
Medium, accessed on August 21, 2025,
https://medium.com/@dranolia/an-authoritative-guide-to-c-design-patterns-7fd
d7681a54f
101.​ State in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/state/cpp/example
102.​ State Software Pattern C++ Examples, accessed on August 21, 2025,
https://softwarepatterns.com/cpp/state-software-pattern-cpp-example
103.​ Strategy - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/strategy
104.​ Template Method - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/template-method
105.​ Visitor in C++ / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/visitor/cpp/example
106.​ Visitor - Refactoring.Guru, accessed on August 21, 2025,
https://refactoring.guru/design-patterns/visitor
107.​ Visitor in C# / Design Patterns - Refactoring.Guru, accessed on August 21,
2025, https://refactoring.guru/design-patterns/visitor/csharp/example
108.​ The Gang of Four :: CC 410 Textbook, accessed on August 21, 2025,
https://textbooks.cs.ksu.edu/cc410/i-oop/09-design-patterns/02-gang-of-four/
109.​ What is the most useful software design pattern you have used in C++? : r/cpp
- Reddit, accessed on August 21, 2025,
https://www.reddit.com/r/cpp/comments/938ncv/what_is_the_most_useful_softw
are_design_pattern/

You might also like