cpp

Design Patterns

03_Design_Patterns⚙️
// Module 10: Expert Topics
// Lesson: High-Impact Design Patterns in Modern C++
// Build: g++ -std=c++17 01_design_patterns.cpp -o patterns_demo
// Description: Implements Strategy, Observer, Factory, and RAII-based patterns.

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

using namespace std;

void printPart(const string &title)
{
    cout << "\n========== " << title << " ==========" << endl;
}

// PART 1: Strategy Pattern for interchangeable algorithms.
class PaymentStrategy
{
public:
    virtual ~PaymentStrategy() = default;
    virtual void pay(double amount) const = 0;
};

class CreditCardPayment : public PaymentStrategy
{
public:
    explicit CreditCardPayment(string number) : number_(move(number)) {}
    void pay(double amount) const override
    {
        cout << "Charging $" << amount << " to credit card ending " << number_.substr(number_.size() - 4) << endl;
    }

private:
    string number_;
};

class CryptoPayment : public PaymentStrategy
{
public:
    explicit CryptoPayment(string wallet) : wallet_(move(wallet)) {}
    void pay(double amount) const override
    {
        cout << "Transferring $" << amount << " via crypto wallet " << wallet_ << endl;
    }

private:
    string wallet_;
};

class CheckoutContext
{
public:
    void setStrategy(unique_ptr<PaymentStrategy> strategy)
    {
        strategy_ = move(strategy);
    }

    void processOrder(double total)
    {
        if (!strategy_)
        {
            throw runtime_error("Payment strategy not set");
        }
        strategy_->pay(total);
    }

private:
    unique_ptr<PaymentStrategy> strategy_;
};

// PART 2: Observer Pattern for event distribution.
class StockObserver
{
public:
    virtual ~StockObserver() = default;
    virtual void onPriceChanged(double newPrice) = 0;
};

class StockTicker
{
public:
    void subscribe(StockObserver *observer)
    {
        observers_.push_back(observer);
    }

    void updatePrice(double price)
    {
        price_ = price;
        for (auto *obs : observers_)
        {
            obs->onPriceChanged(price);
        }
    }

private:
    double price_ = 0.0;
    vector<StockObserver *> observers_;
};

class ConsoleDisplay : public StockObserver
{
public:
    explicit ConsoleDisplay(string name) : name_(move(name)) {}
    void onPriceChanged(double newPrice) override
    {
        cout << name_ << " sees price = " << newPrice << endl;
    }

private:
    string name_;
};

// PART 3: Factory Registry using lambdas and std::function.
class Service
{
public:
    virtual ~Service() = default;
    virtual void execute() = 0;
};

class EmailService : public Service
{
public:
    void execute() override { cout << "Sending notification email." << endl; }
};

class SmsService : public Service
{
public:
    void execute() override { cout << "Sending SMS alert." << endl; }
};

class ServiceFactory
{
public:
    using Creator = function<unique_ptr<Service>()>;

    void registerType(string key, Creator creator)
    {
        registry_[move(key)] = move(creator);
    }

    unique_ptr<Service> create(const string &key) const
    {
        if (auto it = registry_.find(key); it != registry_.end())
        {
            return (it->second)();
        }
        throw runtime_error("Unknown service key: " + key);
    }

private:
    unordered_map<string, Creator> registry_;
};

// PART 4: RAII wrapper for resources (ensures release even with exceptions).
class FileHandle
{
public:
    explicit FileHandle(FILE *file) : file_(file) {}
    ~FileHandle()
    {
        if (file_)
        {
            cout << "Closing file handle." << endl;
            fclose(file_);
        }
    }

    FILE *get() const { return file_; }

    static unique_ptr<FileHandle> open(const char *path, const char *mode)
    {
        FILE *file = fopen(path, mode);
        if (!file)
        {
            throw runtime_error("Failed to open file");
        }
        return make_unique<FileHandle>(file);
    }

private:
    FILE *file_ = nullptr;
};

int main()
{
    printPart("PART 1: Strategy Pattern");
    CheckoutContext checkout;
    checkout.setStrategy(make_unique<CreditCardPayment>("5555444433331111"));
    checkout.processOrder(49.99);
    checkout.setStrategy(make_unique<CryptoPayment>("wallet_xyz"));
    checkout.processOrder(19.95);

    printPart("PART 2: Observer Pattern");
    StockTicker ticker;
    ConsoleDisplay ui("Dashboard");
    ConsoleDisplay logger("Logger");
    ticker.subscribe(&ui);
    ticker.subscribe(&logger);
    ticker.updatePrice(101.5);
    ticker.updatePrice(98.2);

    printPart("PART 3: Factory Registry");
    ServiceFactory factory;
    factory.registerType("email", [] { return make_unique<EmailService>(); });
    factory.registerType("sms", [] { return make_unique<SmsService>(); });
    auto email = factory.create("email");
    auto sms = factory.create("sms");
    email->execute();
    sms->execute();

    printPart("PART 4: RAII Resource Management");
    try
    {
        auto handle = FileHandle::open("patterns_demo.txt", "w");
        fprintf(handle->get(), "Logging pattern demonstrations\n");
        // handle goes out of scope and closes automatically.
    }
    catch (const exception &ex)
    {
        cerr << "Error: " << ex.what() << endl;
    }

    cout << "\nExercises:\n"
         << "1. Add a Flyweight cache to reuse immutable value objects.\n"
         << "2. Implement the Command pattern to support undo/redo in a text editor mock.\n"
         << "3. Convert the Observer into a thread-safe version using weak_ptr.\n"
         << "4. Benchmark shared_ptr vs unique_ptr for Factory-created objects.\n";
    return 0;
}
Design Patterns - C++ Tutorial | DeepML