Introduction
Design patterns are typical solutions to commonly occurring problems in software design. They represent best practices used by experienced object-oriented software developers. Design patterns are like pre-defined blueprints that can be customized to solve recurring design problems in various contexts. In this tutorial, we will explore several fundamental design patterns and implement them in C++.
We will cover the following design patterns:
- Creational Patterns
- Singleton
- Factory Method
- Abstract Factory
- Builder
- Prototype
- Structural Patterns
- Adapter
- Composite
- Proxy
- Flyweight
- Facade
- Bridge
- Decorator
- Behavioral Patterns
- Strategy
- Observer
- Command
- Chain of Responsibility
- State
- Template Method
- Iterator
- Mediator
- Memento
By the end of this tutorial, you will have a comprehensive understanding of these design patterns and how to implement them in C++.
1. Creational Patterns
Singleton
The Singleton pattern ensures that a class has only one instance and provides a global point of access to it. This is useful when exactly one object is needed to coordinate actions across the system.
Implementation:
#include <iostream>
#include <mutex>
class Singleton {
private:
static Singleton* instance;
static std::mutex mtx;
// Private constructor to prevent instantiation.
Singleton() {}
public:
// Static method to get the single instance of the class.
static Singleton* getInstance() {
std::lock_guard<std::mutex> lock(mtx);
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
void showMessage() {
std::cout << "Singleton Instance" << std::endl;
}
};
// Initialize static member variables.
Singleton* Singleton::instance = nullptr;
std::mutex Singleton::mtx;
int main() {
Singleton* s1 = Singleton::getInstance();
s1->showMessage();
Singleton* s2 = Singleton::getInstance();
s2->showMessage();
return 0;
}
Code language: C++ (cpp)
Factory Method
The Factory Method pattern defines an interface for creating an object, but lets subclasses alter the type of objects that will be created.
Implementation:
#include <iostream>
// Product interface
class Product {
public:
virtual void use() = 0;
};
// Concrete Products
class ConcreteProductA : public Product {
public:
void use() override {
std::cout << "Using ConcreteProductA" << std::endl;
}
};
class ConcreteProductB : public Product {
public:
void use() override {
std::cout << "Using ConcreteProductB" << std::endl;
}
};
// Creator class
class Creator {
public:
virtual Product* factoryMethod() = 0;
void anOperation() {
Product* product = factoryMethod();
product->use();
delete product;
}
};
// Concrete Creators
class ConcreteCreatorA : public Creator {
public:
Product* factoryMethod() override {
return new ConcreteProductA();
}
};
class ConcreteCreatorB : public Creator {
public:
Product* factoryMethod() override {
return new ConcreteProductB();
}
};
int main() {
Creator* creatorA = new ConcreteCreatorA();
creatorA->anOperation();
Creator* creatorB = new ConcreteCreatorB();
creatorB->anOperation();
delete creatorA;
delete creatorB;
return 0;
}
Code language: C++ (cpp)
Abstract Factory
The Abstract Factory pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.
Implementation:
#include <iostream>
// Abstract Product A
class AbstractProductA {
public:
virtual void use() = 0;
};
// Abstract Product B
class AbstractProductB {
public:
virtual void use() = 0;
};
// Concrete Product A1
class ConcreteProductA1 : public AbstractProductA {
public:
void use() override {
std::cout << "Using ConcreteProductA1" << std::endl;
}
};
// Concrete Product A2
class ConcreteProductA2 : public AbstractProductA {
public:
void use() override {
std::cout << "Using ConcreteProductA2" << std::endl;
}
};
// Concrete Product B1
class ConcreteProductB1 : public AbstractProductB {
public:
void use() override {
std::cout << "Using ConcreteProductB1" << std::endl;
}
};
// Concrete Product B2
class ConcreteProductB2 : public AbstractProductB {
public:
void use() override {
std::cout << "Using ConcreteProductB2" << std::endl;
}
};
// Abstract Factory
class AbstractFactory {
public:
virtual AbstractProductA* createProductA() = 0;
virtual AbstractProductB* createProductB() = 0;
};
// Concrete Factory 1
class ConcreteFactory1 : public AbstractFactory {
public:
AbstractProductA* createProductA() override {
return new ConcreteProductA1();
}
AbstractProductB* createProductB() override {
return new ConcreteProductB1();
}
};
// Concrete Factory 2
class ConcreteFactory2 : public AbstractFactory {
public:
AbstractProductA* createProductA() override {
return new ConcreteProductA2();
}
AbstractProductB* createProductB() override {
return new ConcreteProductB2();
}
};
int main() {
AbstractFactory* factory1 = new ConcreteFactory1();
AbstractProductA* productA1 = factory1->createProductA();
AbstractProductB* productB1 = factory1->createProductB();
productA1->use();
productB1->use();
delete productA1;
delete productB1;
delete factory1;
AbstractFactory* factory2 = new ConcreteFactory2();
AbstractProductA* productA2 = factory2->createProductA();
AbstractProductB* productB2 = factory2->createProductB();
productA2->use();
productB2->use();
delete productA2;
delete productB2;
delete factory2;
return 0;
}
Code language: C++ (cpp)
Builder
The Builder pattern separates the construction of a complex object from its representation so that the same construction process can create different representations.
Implementation:
#include <iostream>
#include <string>
// Product
class Product {
public:
void setPartA(const std::string& part) { partA = part; }
void setPartB(const std::string& part) { partB = part; }
void setPartC(const std::string& part) { partC = part; }
void show() {
std::cout << "Product Parts: " << partA << ", " << partB << ", " << partC << std::endl;
}
private:
std::string partA;
std::string partB;
std::string partC;
};
// Builder interface
class Builder {
public:
virtual void buildPartA() = 0;
virtual void buildPartB() = 0;
virtual void buildPartC() = 0;
virtual Product* getResult() = 0;
};
// Concrete Builder
class ConcreteBuilder : public Builder {
public:
ConcreteBuilder() { product = new Product(); }
void buildPartA() override {
product->setPartA("PartA1");
}
void buildPartB() override {
product->setPartB("PartB1");
}
void buildPartC() override {
product->setPartC("PartC1");
}
Product* getResult() override {
return product;
}
private:
Product* product;
};
// Director
class Director {
public:
void setBuilder(Builder* b) { builder = b; }
Product* construct() {
builder->buildPartA();
builder->buildPartB();
builder->buildPartC();
return builder->getResult();
}
private:
Builder* builder;
};
int main() {
Director director;
Builder* builder = new ConcreteBuilder();
director.setBuilder(builder);
Product* product = director.construct();
product->show();
delete product;
delete builder;
return 0;
}
Code language: C++ (cpp)
Prototype
The Prototype pattern is used to create a new object by copying an existing object, known as the prototype.
Implementation:
#include <iostream>
#include <unordered_map>
// Prototype interface
class Prototype {
public:
virtual Prototype* clone() const = 0;
virtual void use() const = 0;
virtual ~Prototype() = default;
};
// Concrete Prototype 1
class ConcretePrototype1 : public Prototype {
public:
Prototype* clone() const override {
return new ConcretePrototype1(*this);
}
void use() const override {
std::cout << "Using ConcretePrototype1" << std::endl;
}
};
// Concrete Prototype 2
class ConcretePrototype2 : public Prototype {
public:
Prototype* clone() const override {
return new ConcretePrototype2(*this);
}
void use() const override {
std::cout << "Using ConcretePrototype2" << std::endl;
}
};
// Prototype Factory
class PrototypeFactory {
public:
PrototypeFactory() {
prototypes["type1"] = new ConcretePrototype1();
prototypes["type2"] = new ConcretePrototype2();
}
~PrototypeFactory() {
for (auto& pair : prototypes) {
delete pair.second;
}
}
Prototype* createPrototype(const std
::string& type) {
return prototypes[type]->clone();
}
private:
std::unordered_map<std::string, Prototype*> prototypes;
};
int main() {
PrototypeFactory factory;
Prototype* prototype1 = factory.createPrototype("type1");
prototype1->use();
delete prototype1;
Prototype* prototype2 = factory.createPrototype("type2");
prototype2->use();
delete prototype2;
return 0;
}
Code language: C++ (cpp)
2. Structural Patterns
Adapter
The Adapter pattern allows objects with incompatible interfaces to collaborate. It acts as a bridge between two incompatible interfaces.
Implementation:
#include <iostream>
// Target interface
class Target {
public:
virtual void request() = 0;
virtual ~Target() = default;
};
// Adaptee class with a specific interface
class Adaptee {
public:
void specificRequest() {
std::cout << "Adaptee specific request" << std::endl;
}
};
// Adapter class that makes Adaptee compatible with Target
class Adapter : public Target {
public:
Adapter(Adaptee* adaptee) : adaptee(adaptee) {}
void request() override {
adaptee->specificRequest();
}
private:
Adaptee* adaptee;
};
int main() {
Adaptee* adaptee = new Adaptee();
Target* adapter = new Adapter(adaptee);
adapter->request();
delete adapter;
delete adaptee;
return 0;
}
Code language: C++ (cpp)
Composite
The Composite pattern allows you to compose objects into tree structures to represent part-whole hierarchies. It lets clients treat individual objects and compositions of objects uniformly.
Implementation:
#include <iostream>
#include <vector>
// Component interface
class Component {
public:
virtual void operation() = 0;
virtual void add(Component* component) {}
virtual void remove(Component* component) {}
virtual Component* getChild(int index) { return nullptr; }
virtual ~Component() = default;
};
// Leaf class representing a leaf node in the tree
class Leaf : public Component {
public:
void operation() override {
std::cout << "Leaf operation" << std::endl;
}
};
// Composite class representing a composite node in the tree
class Composite : public Component {
public:
void operation() override {
std::cout << "Composite operation" << std::endl;
for (Component* component : children) {
component->operation();
}
}
void add(Component* component) override {
children.push_back(component);
}
void remove(Component* component) override {
children.erase(std::remove(children.begin(), children.end(), component), children.end());
}
Component* getChild(int index) override {
return children.at(index);
}
private:
std::vector<Component*> children;
};
int main() {
Component* leaf1 = new Leaf();
Component* leaf2 = new Leaf();
Component* composite = new Composite();
composite->add(leaf1);
composite->add(leaf2);
composite->operation();
delete composite;
delete leaf2;
delete leaf1;
return 0;
}
Code language: C++ (cpp)
Proxy
The Proxy pattern provides a surrogate or placeholder for another object to control access to it.
Implementation:
#include <iostream>
// Subject interface
class Subject {
public:
virtual void request() = 0;
virtual ~Subject() = default;
};
// RealSubject class
class RealSubject : public Subject {
public:
void request() override {
std::cout << "RealSubject request" << std::endl;
}
};
// Proxy class
class Proxy : public Subject {
public:
Proxy(RealSubject* realSubject) : realSubject(realSubject) {}
void request() override {
std::cout << "Proxy request" << std::endl;
realSubject->request();
}
private:
RealSubject* realSubject;
};
int main() {
RealSubject* realSubject = new RealSubject();
Proxy* proxy = new Proxy(realSubject);
proxy->request();
delete proxy;
delete realSubject;
return 0;
}
Code language: C++ (cpp)
Flyweight
The Flyweight pattern is used to minimize memory usage or computational expenses by sharing as much as possible with similar objects.
Implementation:
#include <iostream>
#include <unordered_map>
// Flyweight interface
class Flyweight {
public:
virtual void operation(int extrinsicState) = 0;
virtual ~Flyweight() = default;
};
// ConcreteFlyweight class
class ConcreteFlyweight : public Flyweight {
public:
void operation(int extrinsicState) override {
std::cout << "ConcreteFlyweight: " << extrinsicState << std::endl;
}
};
// FlyweightFactory class
class FlyweightFactory {
public:
Flyweight* getFlyweight(const std::string& key) {
if (flyweights.find(key) == flyweights.end()) {
flyweights[key] = new ConcreteFlyweight();
}
return flyweights[key];
}
~FlyweightFactory() {
for (auto& pair : flyweights) {
delete pair.second;
}
}
private:
std::unordered_map<std::string, Flyweight*> flyweights;
};
int main() {
FlyweightFactory factory;
Flyweight* flyweight1 = factory.getFlyweight("key1");
Flyweight* flyweight2 = factory.getFlyweight("key2");
Flyweight* flyweight3 = factory.getFlyweight("key1");
flyweight1->operation(1);
flyweight2->operation(2);
flyweight3->operation(3);
return 0;
}
Code language: C++ (cpp)
Facade
The Facade pattern provides a simplified interface to a complex subsystem.
Implementation:
#include <iostream>
// Subsystem 1
class Subsystem1 {
public:
void operation1() {
std::cout << "Subsystem1 operation1" << std::endl;
}
};
// Subsystem 2
class Subsystem2 {
public:
void operation2() {
std::cout << "Subsystem2 operation2" << std::endl;
}
};
// Facade class
class Facade {
public:
Facade() : subsystem1(new Subsystem1()), subsystem2(new Subsystem2()) {}
~Facade() {
delete subsystem1;
delete subsystem2;
}
void operation() {
subsystem1->operation1();
subsystem2->operation2();
}
private:
Subsystem1* subsystem1;
Subsystem2* subsystem2;
};
int main() {
Facade facade;
facade.operation();
return 0;
}
Code language: C++ (cpp)
Bridge
The Bridge pattern decouples an abstraction from its implementation so that the two can vary independently.
Implementation:
#include <iostream>
// Implementor interface
class Implementor {
public:
virtual void operationImpl() = 0;
virtual ~Implementor() = default;
};
// Concrete Implementor A
class ConcreteImplementorA : public Implementor {
public:
void operationImpl() override {
std::cout << "ConcreteImplementorA operation" << std::endl;
}
};
// Concrete Implementor B
class ConcreteImplementorB : public Implementor {
public:
void operationImpl() override {
std::cout << "ConcreteImplementorB operation" << std::endl;
}
};
// Abstraction class
class Abstraction {
public:
Abstraction(Implementor* impl) : implementor(impl) {}
virtual void operation() {
implementor->operationImpl();
}
protected:
Implementor* implementor;
};
// Refined Abstraction class
class RefinedAbstraction : public Abstraction {
public:
RefinedAbstraction(Implementor* impl) : Abstraction(impl) {}
void operation() override {
implementor->operationImpl();
std::cout << "RefinedAbstraction operation" << std::endl;
}
};
int main() {
Implementor* implA = new ConcreteImplementorA();
Abstraction* abstractionA = new RefinedAbstraction(implA);
abstractionA->operation();
delete abstractionA;
delete implA;
Implementor* implB = new ConcreteImplementorB();
Abstraction* abstractionB = new RefinedAbstraction(implB);
abstractionB->operation();
delete abstractionB;
delete implB;
return 0;
}
Code language: C++ (cpp)
Decorator
The Decorator pattern allows behavior to be added to an individual object, dynamically, without affecting the behavior of other objects from the same class.
Implementation:
#include <iostream>
// Component interface
class Component {
public:
virtual void operation() = 0;
virtual ~Component() = default;
};
// Concrete Component
class ConcreteComponent : public Component {
public:
void operation() override {
std::cout << "ConcreteComponent operation" << std::endl;
}
};
// Decorator class
class Decorator : public Component {
public:
Decorator(Component* component) : component(component) {}
void operation() override {
component->operation();
}
protected:
Component* component;
};
// Concrete Decorator A
class ConcreteDecoratorA : public Decorator {
public:
ConcreteDecoratorA(Component* component) : Decorator(component) {}
void operation() override {
Decorator::operation();
addedBehavior();
}
private:
void addedBehavior() {
std::cout << "ConcreteDecoratorA added behavior" << std::endl;
}
};
// Concrete Decorator B
class ConcreteDecoratorB : public Decorator {
public:
ConcreteDecoratorB(Component* component) : Decorator(component) {}
void operation() override
{
Decorator::operation();
addedBehavior();
}
private:
void addedBehavior() {
std::cout << "ConcreteDecoratorB added behavior" << std::endl;
}
};
int main() {
Component* component = new ConcreteComponent();
Component* decoratorA = new ConcreteDecoratorA(component);
Component* decoratorB = new ConcreteDecoratorB(decoratorA);
decoratorB->operation();
delete decoratorB;
delete decoratorA;
delete component;
return 0;
}
Code language: C++ (cpp)
3. Behavioral Patterns
Strategy
The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable. It lets the algorithm vary independently from clients that use it.
Implementation:
#include <iostream>
// Strategy interface
class Strategy {
public:
virtual void algorithmInterface() = 0;
virtual ~Strategy() = default;
};
// Concrete Strategy A
class ConcreteStrategyA : public Strategy {
public:
void algorithmInterface() override {
std::cout << "ConcreteStrategyA algorithm" << std::endl;
}
};
// Concrete Strategy B
class ConcreteStrategyB : public Strategy {
public:
void algorithmInterface() override {
std::cout << "ConcreteStrategyB algorithm" << std::endl;
}
};
// Context class
class Context {
public:
void setStrategy(Strategy* strategy) {
this->strategy = strategy;
}
void executeStrategy() {
strategy->algorithmInterface();
}
private:
Strategy* strategy;
};
int main() {
Context context;
Strategy* strategyA = new ConcreteStrategyA();
Strategy* strategyB = new ConcreteStrategyB();
context.setStrategy(strategyA);
context.executeStrategy();
context.setStrategy(strategyB);
context.executeStrategy();
delete strategyA;
delete strategyB;
return 0;
}
Code language: C++ (cpp)
Observer
The Observer pattern defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Implementation:
#include <iostream>
#include <vector>
#include <algorithm>
// Observer interface
class Observer {
public:
virtual void update(int state) = 0;
virtual ~Observer() = default;
};
// Subject class
class Subject {
public:
void attach(Observer* observer) {
observers.push_back(observer);
}
void detach(Observer* observer) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify() {
for (Observer* observer : observers) {
observer->update(state);
}
}
void setState(int state) {
this->state = state;
notify();
}
int getState() const {
return state;
}
private:
std::vector<Observer*> observers;
int state;
};
// Concrete Observer A
class ConcreteObserverA : public Observer {
public:
void update(int state) override {
std::cout << "ConcreteObserverA: " << state << std::endl;
}
};
// Concrete Observer B
class ConcreteObserverB : public Observer {
public:
void update(int state) override {
std::cout << "ConcreteObserverB: " << state << std::endl;
}
};
int main() {
Subject subject;
Observer* observerA = new ConcreteObserverA();
Observer* observerB = new ConcreteObserverB();
subject.attach(observerA);
subject.attach(observerB);
subject.setState(1);
subject.setState(2);
subject.detach(observerA);
subject.setState(3);
delete observerA;
delete observerB;
return 0;
}
Code language: C++ (cpp)
Command
The Command pattern encapsulates a request as an object, thereby allowing for parameterization of clients with queues, requests, and operations.
Implementation:
#include <iostream>
#include <vector>
// Command interface
class Command {
public:
virtual void execute() = 0;
virtual ~Command() = default;
};
// Concrete Command
class ConcreteCommand : public Command {
public:
ConcreteCommand(int payload) : payload(payload) {}
void execute() override {
std::cout << "Executing command with payload: " << payload << std::endl;
}
private:
int payload;
};
// Invoker class
class Invoker {
public:
void setCommand(Command* command) {
this->command = command;
}
void executeCommand() {
if (command) {
command->execute();
}
}
private:
Command* command = nullptr;
};
int main() {
Invoker invoker;
Command* command1 = new ConcreteCommand(10);
Command* command2 = new ConcreteCommand(20);
invoker.setCommand(command1);
invoker.executeCommand();
invoker.setCommand(command2);
invoker.executeCommand();
delete command1;
delete command2;
return 0;
}
Code language: C++ (cpp)
Chain of Responsibility
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. Chain the receiving objects and pass the request along the chain until an object handles it.
Implementation:
#include <iostream>
// Handler interface
class Handler {
public:
virtual void setNext(Handler* next) = 0;
virtual void handleRequest(int request) = 0;
virtual ~Handler() = default;
};
// Abstract Handler
class AbstractHandler : public Handler {
public:
AbstractHandler() : nextHandler(nullptr) {}
void setNext(Handler* next) override {
nextHandler = next;
}
void handleRequest(int request) override {
if (nextHandler) {
nextHandler->handleRequest(request);
} else {
std::cout << "Request " << request << " unhandled" << std::endl;
}
}
protected:
Handler* nextHandler;
};
// Concrete Handler 1
class ConcreteHandler1 : public AbstractHandler {
public:
void handleRequest(int request) override {
if (request < 10) {
std::cout << "ConcreteHandler1 handled request " << request << std::endl;
} else {
AbstractHandler::handleRequest(request);
}
}
};
// Concrete Handler 2
class ConcreteHandler2 : public AbstractHandler {
public:
void handleRequest(int request) override {
if (request >= 10 && request < 20) {
std::cout << "ConcreteHandler2 handled request " << request << std::endl;
} else {
AbstractHandler::handleRequest(request);
}
}
};
int main() {
Handler* handler1 = new ConcreteHandler1();
Handler* handler2 = new ConcreteHandler2();
handler1->setNext(handler2);
handler1->handleRequest(5);
handler1->handleRequest(15);
handler1->handleRequest(25);
delete handler1;
delete handler2;
return 0;
}
Code language: C++ (cpp)
State
The State pattern allows an object to alter its behavior when its internal state changes. The object will appear to change its class.
Implementation:
#include <iostream>
// Context class
class Context;
// State interface
class State {
public:
virtual void handle(Context* context) = 0;
virtual ~State() = default;
};
// Concrete State A
class ConcreteStateA : public State {
public:
void handle(Context* context) override;
static ConcreteStateA* getInstance() {
static ConcreteStateA instance;
return &instance;
}
};
// Concrete State B
class ConcreteStateB : public State {
public:
void handle(Context* context) override;
static ConcreteStateB* getInstance() {
static ConcreteStateB instance;
return &instance;
}
};
// Context class
class Context {
public:
Context() : state(ConcreteStateA::getInstance()) {}
void setState(State* state) {
this->state = state;
}
void request() {
state->handle(this);
}
private:
State* state;
};
void ConcreteStateA::handle(Context* context) {
std::cout << "ConcreteStateA handling request" << std::endl;
context->setState(ConcreteStateB::getInstance());
}
void ConcreteStateB::handle(Context* context) {
std::cout << "ConcreteStateB handling request" << std::endl;
context->setState(ConcreteStateA::getInstance());
}
int main() {
Context context;
context.request();
context.request();
context.request();
return 0;
}
Code language: C++ (cpp)
Template Method
The Template Method pattern defines the skeleton of an algorithm in an operation, deferring some steps to subclasses. It lets subclasses redefine certain steps of an algorithm without changing the algorithm’s structure.
Implementation:
#include <iostream>
// Abstract Class
class AbstractClass {
public:
void templateMethod() {
primitiveOperation1();
primitiveOperation2();
}
protected:
virtual void primitiveOperation1() = 0;
virtual void primitiveOperation2() = 0;
virtual ~AbstractClass() = default;
};
// Concrete Class
class ConcreteClass : public AbstractClass {
protected:
void primitiveOperation1() override {
std::cout << "ConcreteClass primitiveOperation1" << std::endl;
}
void primitiveOperation2() override {
std::cout << "ConcreteClass primitiveOperation2" << std::endl;
}
};
int main() {
AbstractClass* instance = new ConcreteClass();
instance->templateMethod();
delete instance;
return 0;
}
Code language: C++ (cpp)
Iterator
The Iterator pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
Implementation
#include <iostream>
#include <vector>
// Iterator interface
template <typename T>
class Iterator {
public:
virtual bool hasNext() = 0;
virtual T next() = 0;
virtual ~Iterator() = default;
};
// Concrete Iterator
template <typename T>
class ConcreteIterator : public Iterator<T> {
public:
ConcreteIterator(const std::vector<T>& collection) : collection(collection), index(0) {}
bool hasNext() override {
return index < collection.size();
}
T next() override {
return collection[index++];
}
private:
std::vector<T> collection;
size_t index;
};
// Aggregate interface
template <typename T>
class Aggregate {
public:
virtual Iterator<T>* createIterator() = 0;
virtual ~Aggregate() = default;
};
// Concrete Aggregate
template <typename T>
class ConcreteAggregate : public Aggregate<T> {
public:
ConcreteAggregate(const std::vector<T>& collection) : collection(collection) {}
Iterator<T>* createIterator() override {
return new ConcreteIterator<T>(collection);
}
private:
std::vector<T> collection;
};
int main() {
std::vector<int> collection = {1, 2, 3, 4, 5};
Aggregate<int>* aggregate = new ConcreteAggregate<int>(collection);
Iterator<int>* iterator = aggregate->createIterator();
while (iterator->hasNext()) {
std::cout << iterator->next() << " ";
}
delete iterator;
delete aggregate;
return 0;
}
Code language: C++ (cpp)
Mediator
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 lets you vary their interaction independently.
Implementation:
#include <iostream>
#include <string>
#include <vector>
class Colleague;
// Mediator interface
class Mediator {
public:
virtual void send(const std::string& message, Colleague* colleague) = 0;
virtual ~Mediator() = default;
};
// Colleague class
class Colleague {
public:
Colleague(Mediator* mediator) : mediator(mediator) {}
virtual void send(const std::string& message) {
mediator->send(message, this);
}
virtual void receive(const std::string& message) = 0;
protected:
Mediator* mediator;
};
// Concrete Mediator
class ConcreteMediator : public Mediator {
public:
void addColleague(Colleague* colleague) {
colleagues.push_back(colleague);
}
void send(const std::string& message, Colleague* sender) override {
for (Colleague* colleague : colleagues) {
if (colleague != sender) {
colleague->receive(message);
}
}
}
private:
std::vector<Colleague*> colleagues;
};
// Concrete Colleague 1
class ConcreteColleague1 : public Colleague {
public:
using Colleague::Colleague;
void receive(const std::string& message) override {
std::cout << "ConcreteColleague1 received: " << message << std::endl;
}
};
// Concrete Colleague 2
class ConcreteColleague2 : public Colleague {
public:
using Colleague::Colleague;
void receive(const std::string& message) override {
std::cout << "ConcreteColleague2 received: " << message << std::endl;
}
};
int main() {
ConcreteMediator mediator;
ConcreteColleague1* colleague1 = new ConcreteColleague1(&mediator);
ConcreteColleague2* colleague2 = new ConcreteColleague2(&mediator);
mediator.addColleague(colleague1);
mediator.addColleague(colleague2);
colleague1->send("Hello from Colleague1");
colleague2->send("Hello from Colleague2");
delete colleague1;
delete colleague2;
return 0;
}
Code language: C++ (cpp)
Memento
The Memento pattern provides the ability to restore an object to its previous state (undo via rollback).
Implementation:
#include <iostream>
#include <string>
// Memento class
class Memento {
public:
Memento(const std::string& state) : state(state) {}
std::string getState() const {
return state;
}
private:
std::string state;
};
// Originator class
class Originator {
public:
void setState(const std::string& state) {
this->state = state;
std::cout << "State set to: " << state << std::endl;
}
std::string getState() const {
return state;
}
Memento* saveStateToMemento() {
return new Memento(state);
}
void getStateFromMemento(Memento* memento) {
state = memento->getState();
}
private:
std::string state;
};
// Caretaker class
class Caretaker {
public:
void addMemento(Memento* memento) {
mementos.push_back(memento);
}
Memento* getMemento(int index) {
return mementos[index];
}
~Caretaker() {
for (Memento* memento : mementos) {
delete memento;
}
}
private:
std::vector<Memento*> mementos;
};
int main() {
Originator originator;
Caretaker caretaker;
originator.setState("State1");
caretaker.addMemento(originator.saveStateToMemento());
originator.setState("State2");
caretaker.addMemento(originator.saveStateToMemento());
originator.setState("State3");
originator.getStateFromMemento(caretaker.getMemento(0));
std::cout << "First saved state: " << originator.getState() << std::endl;
originator.getStateFromMemento(caretaker.getMemento(1));
std::cout << "Second saved state: " << originator.getState() << std::endl;
return 0;
}
Code language: C++ (cpp)
Conclusion
Design patterns are an essential toolkit for any software developer. They provide tested, proven development paradigms that enhance code readability, maintainability, and reusability. In this tutorial, we’ve covered various creational, structural, and behavioral design patterns, explaining their concepts and providing C++ implementations.
As you continue to grow as a developer, you’ll find that understanding and applying these patterns will help you solve complex design problems more efficiently. Practice implementing these patterns in your projects, and you’ll gain a deeper insight into their practical benefits and usage.