cpp
design patterns
01_design_patterns.cpp⚙️cpp
// 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;
}