What You'll Learn
- Thread-safe Singleton pattern
- Factory pattern with unique_ptr
- Observer with std::function
- Strategy pattern for swappable algorithms
Design Patterns in C++
Design patterns are reusable solutions to common software design problems. This lesson covers four of the most important patterns — Singleton, Factory, Observer, and Strategy — implemented idiomatically in modern C++ with smart pointers, lambdas, and std::function.
Singleton — One Instance, Global Access
The Singleton ensures exactly one instance of a class exists. In C++11+, Meyer's Singleton uses a local static variable — the compiler guarantees thread-safe initialization. Delete the copy constructor and assignment operator to prevent duplicates.
Common Mistake: Overusing Singleton. It introduces global state and makes testing harder. Use it only for truly global resources like loggers or configuration managers.
Meyer's Singleton
Thread-safe singleton with deleted copy operations
#include <iostream>
#include <string>
using namespace std;
// Thread-safe Singleton (Meyer's Singleton)
class Logger {
string logFile;
Logger(const string& file) : logFile(file) {
cout << "Logger created for: " << file << endl;
}
public:
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
static Logger& instance() {
static Logger inst("app.log"); // thread-safe since C++11
return inst;
}
void log(const string& msg
...Factory — Create Without Knowing the Type
The Factory pattern decouples object creation from usage. Callers ask for a shape by name; the factory returns a unique_ptr<Shape>. The caller never needs to know which concrete class was created.
Factory Pattern
Create shapes through a factory function
#include <iostream>
#include <memory>
#include <string>
using namespace std;
// Abstract product
class Shape {
public:
virtual void draw() const = 0;
virtual double area() const = 0;
virtual ~Shape() = default;
};
class Circle : public Shape {
double radius;
public:
Circle(double r) : radius(r) {}
void draw() const override {
cout << "Drawing circle (r=" << radius << ")" << endl;
}
double area() const override { return 3.14159 * radius * radius; }
};
cl
...Observer — React to Events
The Observer pattern lets objects subscribe to events without tight coupling. Modern C++ replaces the classic virtual-method approach with std::function and lambdas for a cleaner, more flexible implementation.
Pro Tip: For thread-safe observers, protect the listener list with a mutex and consider using weak_ptr to avoid dangling references when observers are destroyed.
Observer Pattern
Event-driven communication with std::function
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
// Observer pattern with std::function
class EventEmitter {
using Callback = function<void(const string&)>;
vector<pair<string, Callback>> listeners;
public:
void on(const string& event, Callback cb) {
listeners.push_back({event, cb});
}
void emit(const string& event, const string& data) {
cout << "--- Emitting: " << event << " ---" << endl
...Strategy — Swap Algorithms at Runtime
The Strategy pattern encapsulates algorithms behind a common interface so you can swap them at runtime. With std::function, you don't even need an interface class — any callable with the right signature works.
Strategy Pattern
Swap sorting algorithms at runtime
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
// Strategy pattern — swap algorithms at runtime
using SortStrategy = function<void(vector<int>&)>;
void bubbleSort(vector<int>& v) {
cout << "Using Bubble Sort" << endl;
for (size_t i = 0; i < v.size(); i++)
for (size_t j = 0; j + 1 < v.size() - i; j++)
if (v[j] > v[j+1]) swap(v[j], v[j+1]);
}
void stdSort(vector<int>& v) {
cout << "Using std::sort (IntroSort)
...Quick Reference
| Pattern | Purpose | C++ Idiom |
|---|---|---|
| Singleton | Single global instance | static local variable |
| Factory | Decouple creation | unique_ptr + factory function |
| Observer | Event notification | std::function callbacks |
| Strategy | Swappable algorithms | std::function / lambdas |
Lesson Complete!
You can now implement Singleton, Factory, Observer, and Strategy patterns idiomatically in modern C++.
Sign up for free to track which lessons you've completed and get learning reminders.