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

    Try it Yourself »
    C++
    #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

    Try it Yourself »
    C++
    #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

    Try it Yourself »
    C++
    #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

    Try it Yourself »
    C++
    #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

    PatternPurposeC++ Idiom
    SingletonSingle global instancestatic local variable
    FactoryDecouple creationunique_ptr + factory function
    ObserverEvent notificationstd::function callbacks
    StrategySwappable algorithmsstd::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.

    Previous

    Cookie & Privacy Settings

    We use cookies to improve your experience, analyze traffic, and show personalized ads. You can manage your preferences below.

    By clicking "Accept All", you consent to our use of cookies for analytics and personalized advertising. You can customize your preferences or reject non-essential cookies.

    Privacy PolicyTerms of Service