Code360 powered by Coding Ninjas X Naukri.com. Code360 powered by Coding Ninjas X Naukri.com
Table of contents
1.
Introduction
2.
Creational Patterns
2.1.
Singleton Pattern
2.2.
Factory Method Pattern
2.3.
Abstract Factory Pattern
2.4.
Builder Pattern
2.5.
Prototype Pattern
3.
Structural Patterns
3.1.
Adapter Pattern
3.2.
C++
3.3.
Bridge Pattern
3.3.1.
Example : 
3.4.
C++
3.5.
Composite Pattern
3.6.
Decorator Pattern
3.7.
Facade Pattern
3.8.
Flyweight Pattern
4.
Behavioral Patterns
5.
Observer Pattern
6.
Strategy Pattern
6.1.
Example
6.2.
C++
7.
Template Method Pattern
8.
Visitor Pattern
8.1.
Chain of Responsibility Pattern
8.2.
Example
9.
Command Pattern
9.1.
Example 
10.
Interpreter Pattern
10.1.
Example
11.
Iterator Pattern
11.1.
Example
12.
Mediator Pattern
12.1.
Example
13.
Memento Pattern
13.1.
Example
14.
Observer Pattern
14.1.
Example 
15.
State Pattern
15.1.
Example
16.
Strategy Pattern
16.1.
Example
17.
Template Method Pattern
17.1.
Example
18.
Frequently Asked Questions
18.1.
What are design patterns & why are they important in C++?
18.2.
Can design patterns be mixed in a single application?
18.3.
How do I choose the right design pattern for my project?
19.
Conclusion
Last Updated: Jun 12, 2024
Easy

Design Patterns in C++

Author Ravi Khorwal
0 upvote
Master Python: Predicting weather forecasts
Speaker
Ashwin Goyal
Product Manager @

Introduction

Design patterns are reusable solutions to solve basic and common problems in software design. They provide tried & tested approaches to solve specific design problems, which makes code more flexible, maintainable, & efficient. In C++, design patterns are widely used to create robust & scalable applications.

Design Patterns in C++

In this article, we will discuss various design patterns in C++, which includes creational, structural, & behavioral patterns with their respective codes examples to show their implementation.

Creational Patterns

Creational patterns deal with object creation mechanisms, trying to create objects in a way that suits the situation. They help make a system independent of how its objects are created, composed, & represented. Some common creational patterns in C++ are:

Singleton Pattern

Ensures a class has only one instance & provides a global point of access to it.This is very useful in scenarios where a single object is needed to coordinate actions across the system.

class Singleton {
private:
    static Singleton* instance;
    Singleton() {}
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
};
Singleton* Singleton::instance = nullptr;

Factory Method Pattern

Defines an interface for creating an object, but lets subclasses decide which class to instantiate. This pattern is useful when there are multiple derivatives of a common base class.

class Product {
public:
    virtual ~Product() {}
    virtual std::string Operation() const = 0;
};
class ConcreteProduct1 : public Product {
public:
    std::string Operation() const override {
        return "Result of ConcreteProduct1";
    }
};
class ConcreteProduct2 : public Product {
public:
    std::string Operation() const override {
        return "Result of ConcreteProduct2";
    }
};

Abstract Factory Pattern

Provides an interface for creating families of related or dependent objects without specifying their concrete classes.

Builder Pattern

Separates the construction of a complex object from its representation, allowing the same construction process to create various representations.

Prototype Pattern

Specifies the kind of objects to create using a prototypical instance, & creates new objects by copying this prototype.

Get the tech career you deserve, faster!
Connect with our expert counsellors to understand how to hack your way to success
User rating 4.7/5
1:1 doubt support
95% placement record
Akash Pal
Senior Software Engineer
326% Hike After Job Bootcamp
Himanshu Gusain
Programmer Analyst
32 LPA After Job Bootcamp
After Job
Bootcamp

Structural Patterns

Structural patterns are concerned with how classes & objects are composed to form larger structures. They use inheritance to compose interfaces or implementations. Some common structural patterns in C++ are:

Adapter Pattern

Allows objects with incompatible interfaces to collaborate by converting the interface of a class into another interface that clients expect.This is useful when trying to integrate classes that couldn't otherwise work together due to incompatible interfaces.

  • C++

C++

#include <iostream>
#include <string>
#include <algorithm>

// Target class
class Target {
public:
virtual ~Target() = default;
virtual std::string Request() const {
return "Target: The default target's behavior.";
}
};

// Adaptee class
class Adaptee {
public:
std::string SpecificRequest() const {
return ".eetpadA eht fo roivaheb laicepS";
}
};

// Adapter class
class Adapter : public Target {
private:
Adaptee* adaptee_;
public:
Adapter(Adaptee* adaptee) : adaptee_(adaptee) {}
std::string Request() const override {
std::string to_reverse = this->adaptee_->SpecificRequest();
std::reverse(to_reverse.begin(), to_reverse.end());
return "Adapter: (TRANSLATED) " + to_reverse;
}
};

// Main function to demonstrate the Adapter pattern
int main() {
Adaptee* adaptee = new Adaptee();
Target* target = new Adapter(adaptee);

std::cout << "Target: " << target->Request() << std::endl;

delete target;
delete adaptee;

return 0;
}

Output

Target: Adapter: (TRANSLATED) Special behavior of the Adaptee.

Bridge Pattern

Decouples an abstraction from its implementation so that the two can vary independently.

Example : 

  • C++

C++

#include <iostream>
#include <memory>

// Implementor
class Implementor {
public:
virtual void implementation() const = 0;
virtual ~Implementor() {}
};

// ConcreteImplementorA
class ConcreteImplementorA : public Implementor {
public:
void implementation() const override {
std::cout << "Implementation by ConcreteImplementorA" << std::endl;
}
};

// ConcreteImplementorB
class ConcreteImplementorB : public Implementor {
public:
void implementation() const override {
std::cout << "Implementation by ConcreteImplementorB" << std::endl;
}
};

// Abstraction
class Abstraction {
protected:
std::unique_ptr<Implementor> implementor;

public:
Abstraction(std::unique_ptr<Implementor> impl) : implementor(std::move(impl)) {}
virtual void operation() const {
implementor->implementation();
}
virtual ~Abstraction() {}
};

// RefinedAbstraction
class RefinedAbstraction : public Abstraction {
public:
RefinedAbstraction(std::unique_ptr<Implementor> impl) : Abstraction(std::move(impl)) {}
void operation() const override {
std::cout << "RefinedAbstraction operation:" << std::endl;
implementor->implementation();
}
};

int main() {
std::unique_ptr<Implementor> impA = std::make_unique<ConcreteImplementorA>();
std::unique_ptr<Abstraction> absA = std::make_unique<RefinedAbstraction>(std::move(impA));
absA->operation();

std::unique_ptr<Implementor> impB = std::make_unique<ConcreteImplementorB>();
std::unique_ptr<Abstraction> absB = std::make_unique<RefinedAbstraction>(std::move(impB));
absB->operation();

return 0;
}

Output

RefinedAbstraction operation:
Implementation by ConcreteImplementorA
RefinedAbstraction operation:
Implementation by ConcreteImplementorB

Composite Pattern

Composes objects into tree structures to represent part-whole hierarchies, allowing clients to treat individual objects & compositions uniformly.

Decorator Pattern

Attaches additional responsibilities to an object dynamically, providing a flexible alternative to subclassing for extending functionality.

Facade Pattern

Provides a unified interface to a set of interfaces in a subsystem, defining a higher-level interface that makes the subsystem easier to use.

Flyweight Pattern

Uses sharing to support large numbers of fine-grained objects efficiently.

Proxy Pattern: Provides a surrogate or placeholder for another object to control access to it.

Behavioral Patterns

Behavioral patterns are concerned with algorithms & the assignment of responsibilities between objects. They describe not just patterns of objects or classes but also the patterns of communication between them. Some common behavioral patterns in C++ are:

Observer Pattern

Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified & updated automatically.

class Subject;
class Observer {
public:
    virtual ~Observer() {}
    virtual void Update(Subject* subject) = 0;
};
class Subject {
private:
    std::list<Observer*> observers_;
public:
    virtual ~Subject() {}
    void Attach(Observer* observer) {
        observers_.push_back(observer);
    }
    void Detach(Observer* observer) {
        observers_.remove(observer);
    }
    void Notify() {
        for (Observer* observer : observers_) {
            observer->Update(this);
        }
    }
};
class ConcreteSubject : public Subject {
private:
    std::string state_;
public:
    std::string GetState() const {
        return state_;
    }
    void SetState(const std::string& state) {
        state_ = state;
        Notify();
    }
};
class ConcreteObserver : public Observer {
private:
    std::string name_;
    ConcreteSubject* subject_;
public:
    ConcreteObserver(const std::string& name, ConcreteSubject* subject)
        : name_(name), subject_(subject) {
        subject_->Attach(this);
    }
    ~ConcreteObserver() {
        subject_->Detach(this);
    }
    void Update(Subject* subject) override {
        if (subject == subject_) {
            std::cout << name_ << ": " << subject_->GetState() << std::endl;
        }
    }
};

Strategy Pattern

Defines a family of algorithms, encapsulates each one, & makes them interchangeable, allowing the algorithm to vary independently from clients that use it.

Example

  • C++

C++

#include <iostream>
#include <memory>

class Strategy {
public:
virtual void execute() const = 0;
virtual ~Strategy() {}
};

class ConcreteStrategyA : public Strategy {
public:
void execute() const override {
std::cout << "Executing strategy A." << std::endl;
}
};

class ConcreteStrategyB : public Strategy {
public:
void execute() const override {
std::cout << "Executing strategy B." << std::endl;
}
};

class Context {
private:
std::unique_ptr<Strategy> strategy;

public:
void setStrategy(std::unique_ptr<Strategy> newStrategy) {
strategy = std::move(newStrategy);
}

void performStrategy() const {
if (strategy) {
strategy->execute();
}
}
};

int main() {
Context context;
context.setStrategy(std::make_unique<ConcreteStrategyA>());
context.performStrategy();

context.setStrategy(std::make_unique<ConcreteStrategyB>());
context.performStrategy();

return 0;
}

Output

Executing strategy A.
Executing strategy B

Template Method Pattern

Defines the skeleton of an algorithm in a method, deferring some steps to subclasses, allowing subclasses to redefine certain steps of the algorithm without changing its structure.

Visitor Pattern

Represents an operation to be performed on the elements of an object structure, letting you define a new operation without changing the classes of the elements on which it operates.

Chain of Responsibility Pattern

The Chain of Responsibility pattern is a behavioral design pattern that lets you pass requests along a chain of handlers. Upon receiving a request, each handler decides either to process the request or to pass it to the next handler in the chain.

Example

class Handler {
public:
    Handler(Handler* nextHandler) : nextHandler_(nextHandler) {}
    virtual ~Handler() {}
    virtual void HandleRequest(int request) {
        if (nextHandler_) {
            nextHandler_->HandleRequest(request);
        }
    }
protected:
    Handler* nextHandler_;
};
class ConcreteHandler1 : public Handler {
public:
    ConcreteHandler1(Handler* nextHandler) : Handler(nextHandler) {}
    void HandleRequest(int request) override {
        if (request >= 0 && request < 10) {
            std::cout << "ConcreteHandler1 handled request " << request << std::endl;
        } else {
            Handler::HandleRequest(request);
        }
    }
};
class ConcreteHandler2 : public Handler {
public:
    ConcreteHandler2(Handler* nextHandler) : Handler(nextHandler) {}
    void HandleRequest(int request) override {
        if (request >= 10 && request < 20) {
            std::cout << "ConcreteHandler2 handled request " << request << std::endl;
        } else {
            Handler::HandleRequest(request);
        }
    }
};


In this example, the Handler class defines the interface for handling requests & maintains a reference to the next handler in the chain. The ConcreteHandler1 & ConcreteHandler2 classes are concrete implementations of the Handler class that handle requests within a specific range. If a handler cannot process the request, it passes the request to the next handler in the chain.

Command Pattern

The Command pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation lets you pass requests as a method arguments, delay or queue a request's execution, & support undoable operations.

Example 

class Command {
public:
    virtual ~Command() {}
    virtual void Execute() const = 0;
};
class SimpleCommand : public Command {
private:
    std::string pay_load_;
public:
    explicit SimpleCommand(std::string pay_load) : pay_load_(pay_load) {}
    void Execute() const override {
        std::cout << "SimpleCommand: See, I can do simple things like printing (" << pay_load_ << ")" << std::endl;
    }
};
class Receiver {
public:
    void DoSomething(const std::string& a) {
        std::cout << "Receiver: Working on (" << a << ".)" << std::endl;
    }
    void DoSomethingElse(const std::string& b) {
        std::cout << "Receiver: Also working on (" << b << ".)" << std::endl;
    }
};
class ComplexCommand : public Command {
private:
    Receiver* receiver_;
    std::string a_;
    std::string b_;
public:
    ComplexCommand(Receiver* receiver, std::string a, std::string b) : receiver_(receiver), a_(a), b_(b) {}
    void Execute() const override {
        std::cout << "ComplexCommand: Complex stuff should be done by a receiver object." << std::endl;
        receiver_->DoSomething(a_);
        receiver_->DoSomethingElse(b_);
    }
};
class Invoker {
private:
    Command* on_start_;
    Command* on_finish_;
public:
    ~Invoker() {
        delete on_start_;
        delete on_finish_;
    }
    void SetOnStart(Command* command) {
        on_start_ = command;
    }
    void SetOnFinish(Command* command) {
        on_finish_ = command;
    }
    void DoSomethingImportant() {
        std::cout << "Invoker: Does anybody want something done before I begin?" << std::endl;
        if (on_start_) {
            on_start_->Execute();
        }
        std::cout << "Invoker: ...doing something really important..." << std::endl;
        std::cout << "Invoker: Does anybody want something done after I finish?" << std::endl;
        if (on_finish_) {
            on_finish_->Execute();
        }
    }
};


In this example, the Command class is the abstract base class that declares the Execute method. The SimpleCommand & ComplexCommand classes are concrete implementations of the Command class. The Receiver class contains some business logic that the commands can execute. The Invoker class is responsible for executing the commands.

Interpreter Pattern

The Interpreter pattern is a behavioral design pattern that defines a grammatical representation for a language & provides an interpreter to deal with this grammar. It's used to solve frequently occurring problems like parsing & evaluating expressions, or matching text against patterns.

Example

class Context;

class Expression {
public:
    virtual ~Expression() {}
    virtual bool Interpret(const Context& context) const = 0;
};

class TerminalExpression : public Expression {
private:
    std::string data_;
public:
    TerminalExpression(const std::string& data) : data_(data) {}
    bool Interpret(const Context& context) const override {
        std::string input = context.GetInput();
        return input.find(data_) != std::string::npos;
    }
};

class OrExpression : public Expression {
private:
    Expression* expr1_;
    Expression* expr2_;
public:
    OrExpression(Expression* expr1, Expression* expr2) : expr1_(expr1), expr2_(expr2) {}
    ~OrExpression() {
        delete expr1_;
        delete expr2_;
    }
    bool Interpret(const Context& context) const override {
        return expr1_->Interpret(context) || expr2_->Interpret(context);
    }
};

class AndExpression : public Expression {
private:
    Expression* expr1_;
    Expression* expr2_;
public:
    AndExpression(Expression* expr1, Expression* expr2) : expr1_(expr1), expr2_(expr2) {}
    ~AndExpression() {
        delete expr1_;
        delete expr2_;
    }
    bool Interpret(const Context& context) const override {
        return expr1_->Interpret(context) && expr2_->Interpret(context);
    }
};

class Context {
private:
    std::string input_;
public:
    Context(const std::string& input) : input_(input) {}
    std::string GetInput() const {
        return input_;
    }
};


In this example, the Expression class is the abstract base class that declares the Interpret method. The TerminalExpression, OrExpression, & AndExpression classes are concrete implementations of the Expression class. The Context class contains the input string that the expressions will interpret.

Note : The Interpreter pattern is useful when you need to interpret a language grammar or interpret a set of rules. It's often used in SQL parsing, symbol processing engines, & compilers.

Iterator Pattern

The Iterator pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. It defines an iterator that encapsulates the internal structure of how the iteration occurs.

Example

template <typename T>
class Iterator {
public:
    virtual ~Iterator() {}
    virtual void First() = 0;
    virtual void Next() = 0;
    virtual bool IsDone() const = 0;
    virtual T CurrentItem() const = 0;
};

template <typename T>
class ConcreteIterator : public Iterator<T> {
private:
    T* items_;
    int index_;
    int size_;
public:
    ConcreteIterator(T* items, int size) : items_(items), index_(0), size_(size) {}
    void First() override {
        index_ = 0;
    }
    void Next() override {
        index_++;
    }
    bool IsDone() const override {
        return index_ >= size_;
    }
    T CurrentItem() const override {
        return items_[index_];
    }
};

template <typename T>
class Aggregate {
public:
    virtual ~Aggregate() {}
    virtual Iterator<T>* CreateIterator() const = 0;
};

template <typename T>
class ConcreteAggregate : public Aggregate<T> {
private:
    T* items_;
    int size_;
public:
    ConcreteAggregate(T* items, int size) : items_(items), size_(size) {}
    Iterator<T>* CreateIterator() const override {
        return new ConcreteIterator<T>(items_, size_);
    }
};


In this example, the Iterator class is the abstract base class that declares the interface for accessing & traversing elements. The ConcreteIterator class is a concrete implementation of the Iterator class. The Aggregate class is the abstract base class that declares the interface for creating an Iterator object. The ConcreteAggregate class is a concrete implementation of the Aggregate class.

Note : The Iterator pattern is useful when you want to provide a standard way to iterate over a collection of objects without exposing the collection's internal structure. It also allows you to support multiple traversals of the same collection simultaneously.

Mediator Pattern

The Mediator pattern is a behavioral design pattern that lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects & forces them to collaborate only via a mediator object.

Example

class BaseComponent;
class Mediator {
public:
    virtual void Notify(BaseComponent* sender, std::string event) const = 0;
};

class BaseComponent {
protected:
    Mediator* mediator_;
public:
    BaseComponent(Mediator* mediator = nullptr) : mediator_(mediator) {}
    void SetMediator(Mediator* mediator) {
        mediator_ = mediator;
    }
};

class Component1 : public BaseComponent {
public:
    void DoA() {
        std::cout << "Component 1 does A." << std::endl;
        mediator_->Notify(this, "A");
    }
    void DoB() {
        std::cout << "Component 1 does B." << std::endl;
        mediator_->Notify(this, "B");
    }
};

class Component2 : public BaseComponent {
public:
    void DoC() {
        std::cout << "Component 2 does C." << std::endl;
        mediator_->Notify(this, "C");
    }
    void DoD() {
        std::cout << "Component 2 does D." << std::endl;
        mediator_->Notify(this, "D");
    }
};

class ConcreteMediator : public Mediator {
private:
    Component1* component1_;
    Component2* component2_;
public:
    ConcreteMediator(Component1* c1, Component2* c2) : component1_(c1), component2_(c2) {
        component1_->SetMediator(this);
        component2_->SetMediator(this);
    }
    void Notify(BaseComponent* sender, std::string event) const override {
        if (event == "A") {
            std::cout << "Mediator reacts on A & triggers following operations:" << std::endl;
            component2_->DoC();
        }
        if (event == "D") {
            std::cout << "Mediator reacts on D & triggers following operations:" << std::endl;
            component1_->DoB();
            component2_->DoC();
        }
    }
};


In this example, the Mediator class declares the interface for communication between Component objects. The BaseComponent class defines the interface for components that can communicate through a mediator. The Component1 & Component2 classes are concrete components that communicate through the mediator. The ConcreteMediator class implements cooperative behavior by coordinating several components.

Note : The Mediator pattern is useful when you want to centralize complex communications & control logic between related objects, making it easier to understand, maintain, & extend.

Memento Pattern

The Memento pattern is a behavioral design pattern that lets you save & restore the previous state of an object without revealing the details of its implementation. It introduces three actor classes: the originator, a caretaker & a memento.

  • The originator is the object whose state needs to be saved & restored. It creates a memento containing a snapshot of its current internal state & uses the memento to restore its internal state.
     
  • The memento is an object that stores the originator's internal state. The memento must not expose its state to any object except the originator.
     
  • The caretaker is responsible for the memento's safekeeping. It never operates on or examines the contents of a memento.

Example

class Memento {
private:
    std::string state_;
public:
    Memento(std::string state) : state_(state) {}
    std::string GetState() const {
        return state_;
    }
};
class Originator {
private:
    std::string state_;
public:
    void SetState(const std::string& state) {
        state_ = state;
        std::cout << "Originator: Setting state to " << state_ << std::endl;
    }
    Memento SaveStateToMemento() {
        std::cout << "Originator: Saving state to Memento." << std::endl;
        return Memento(state_);
    }
    void GetStateFromMemento(const Memento& memento) {
        state_ = memento.GetState();
        std::cout << "Originator: State after restoring from Memento: " << state_ << std::endl;
    }
};
class Caretaker {
private:
    std::vector<Memento> mementos_;
    Originator* originator_;
public:
    Caretaker(Originator* originator) : originator_(originator) {}
    void Backup() {
        std::cout << "Caretaker: Saving Originator's state." << std::endl;
        mementos_.push_back(originator_->SaveStateToMemento());
    }
    void Undo() {
        if (mementos_.empty()) {
            return;
        }
        Memento memento = mementos_.back();
        mementos_.pop_back();
        std::cout << "Caretaker: Restoring state to: " << memento.GetState() << std::endl;
        originator_->GetStateFromMemento(memento);
    }
};


In this example, the Memento class stores the state of the Originator object. The Originator class defines methods for saving & restoring its state using a Memento object. The Caretaker class is responsible for storing & providing mementos to the originator when needed for restoration.

Note : The Memento pattern is useful when you need to provide an undo mechanism in your application, when the state of an object may need to be restored to a previous state, or when direct access to the object's fields/getters/setters violates its encapsulation.

Observer Pattern

The Observer pattern is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they're observing.

The pattern has two main components:

  • Subject (also known as Publisher) - The object that has some interesting state & notifies observers when the state changes.
     
  • Observer (also known as Subscriber) - The objects that want to track changes to the publisher's state.

Example 

class IObserver {
public:
    virtual ~IObserver() {};
    virtual void Update(const std::string& message_from_subject) = 0;
};
class ISubject {
public:
    virtual ~ISubject() {};
    virtual void Attach(IObserver* observer) = 0;
    virtual void Detach(IObserver* observer) = 0;
    virtual void Notify() = 0;
};
class Subject : public ISubject {
public:
    virtual ~Subject() {
        std::cout << "Goodbye, I was the Subject." << std::endl;
    }
    void Attach(IObserver* observer) override {
        list_observer_.push_back(observer);
    }
    void Detach(IObserver* observer) override {
        list_observer_.remove(observer);
    }
    void Notify() override {
        std::list<IObserver*>::iterator iterator = list_observer_.begin();
        while (iterator != list_observer_.end()) {
            (*iterator)->Update(message_);
            ++iterator;
        }
    }
    void CreateMessage(std::string message = "Empty") {
        message_ = message;
        Notify();
    }
private:
    std::list<IObserver*> list_observer_;
    std::string message_;
};
class Observer : public IObserver {
public:
    Observer(Subject& subject) : subject_(subject) {
        subject_.Attach(this);
        std::cout << "Hi, I'm the Observer \"" << ++Observer::static_number_ << "\"." << std::endl;
        number_ = Observer::static_number_;
    }
    virtual ~Observer() {
        std::cout << "Goodbye, I was the Observer \"" << number_ << "\"." << std::endl;
    }
    void Update(const std::string& message_from_subject) override {
        message_from_subject_ = message_from_subject;
        PrintInfo();
    }
    void PrintInfo() {
        std::cout << "Observer \"" << number_ << "\": a new message is available --> " << message_from_subject_ << std::endl;
    }
private:
    std::string message_from_subject_;
    Subject& subject_;
    static int static_number_;
    int number_;
};
int Observer::static_number_ = 0;


In this example, the ISubject class is the interface that defines the operations for attaching & detaching observers, as well as notifying them. The Subject class is the concrete implementation of ISubject. The IObserver class is the interface that defines the update operation. The Observer class is the concrete implementation of IObserver.

Note : The Observer pattern is useful when changes to the state of one object may require changing other objects, & the actual set of objects is unknown beforehand or changes dynamically. It lets you vary subjects & observers independently, so you can reuse subjects without reusing their observers, & vice versa.

State Pattern

The State pattern is a behavioral design pattern that lets an object alter its behavior when its internal state changes. It appears as if the object changed its class.

The pattern defines a set of state classes that represent various states of the context object. The context object's behavior changes based on its current state object.

Example

class Context;
class State {
protected:
    Context* context_;
public:
    virtual ~State() {}
    void SetContext(Context* context) {
        context_ = context;
    }
    virtual void Handle1() = 0;
    virtual void Handle2() = 0;
};
class ConcreteStateA : public State {
public:
    void Handle1() override;
    void Handle2() override {
        std::cout << "ConcreteStateA handles request2." << std::endl;
    }
};
class ConcreteStateB : public State {
public:
    void Handle1() override {
        std::cout << "ConcreteStateB handles request1." << std::endl;
    }
    void Handle2() override {
        std::cout << "ConcreteStateB handles request2." << std::endl;
        std::cout << "ConcreteStateB wants to change the state of the context." << std::endl;
        context_->TransitionTo(new ConcreteStateA);
    }
};
class Context {
private:
    State* state_;
public:
    Context(State* state) : state_(nullptr) {
        TransitionTo(state);
    }
    ~Context() {
        delete state_;
    }
    void TransitionTo(State* state) {
        std::cout << "Context: Transition to " << typeid(*state).name() << "." << std::endl;
        if (state_ != nullptr)
            delete state_;
        state_ = state;
        state_->SetContext(this);
    }
    void Request1() {
        state_->Handle1();
    }
    void Request2() {
        state_->Handle2();
    }
};
void ConcreteStateA::Handle1() {
    std::cout << "ConcreteStateA handles request1." << std::endl;
    std::cout << "ConcreteStateA wants to change the state of the context." << std::endl;
    context_->TransitionTo(new ConcreteStateB);
}


In this example, the State class is the abstract base class that defines the interface for encapsulating the behavior associated with a particular state of the Context class. The ConcreteStateA & ConcreteStateB classes are concrete implementations for the different states of the Context. The Context class defines the interface of interest to clients & maintains an instance of a ConcreteState subclass that defines the current state.

Note : The State pattern is useful when an object's behavior depends on its state, & it must change its behavior at run-time depending on that state. It's also useful when operations have large, multipart conditional statements that depend on the object's state.

Strategy Pattern

The Strategy pattern is a behavioral design pattern that lets you define a family of algorithms, put each of them into a separate class, & make their objects interchangeable.

The pattern has three main components:

  • Strategy - The interface common to all supported algorithms. Context uses this interface to call the algorithm defined by a ConcreteStrategy.
     
  • ConcreteStrategy - The concrete classes that implement the algorithm using the Strategy interface.
     
  • Context - The class that's configured with a ConcreteStrategy object & maintains a reference to a Strategy object.

Example

class Strategy {
public:
    virtual ~Strategy() {}
    virtual std::string DoAlgorithm(const std::vector<std::string>& data) const = 0;
};
class ConcreteStrategyA : public Strategy {
public:
    std::string DoAlgorithm(const std::vector<std::string>& data) const override {
        std::string result;
        for (const std::string& item : data) {
            result += item + " ";
        }
        result.pop_back(); // Remove the trailing space
        return result;
    }
};
class ConcreteStrategyB : public Strategy {
public:
    std::string DoAlgorithm(const std::vector<std::string>& data) const override {
        std::string result;
        for (const std::string& item : data) {
            result = item + " " + result;
        }
        result.pop_back(); // Remove the trailing space
        return result;
    }
};
class Context {
private:
    Strategy* strategy_;
public:
    Context(Strategy* strategy = nullptr) : strategy_(strategy) {}
    ~Context() {
        delete strategy_;
    }
    void SetStrategy(Strategy* strategy) {
        delete strategy_;
        strategy_ = strategy;
    }
    void DoSomeBusinessLogic() const {
        std::vector<std::string> data {"a", "b", "c", "d", "e"};
        std::cout << "Context: Sorting data using the strategy (not sure how it'll do it)." << std::endl;
        std::string result = strategy_->DoAlgorithm(data);
        std::cout << result << std::endl;
    }
};


In this example, the Strategy class is the abstract base class that defines the interface for the algorithms. The ConcreteStrategyA & ConcreteStrategyB classes are concrete implementations of the algorithms. The Context class is configured with a ConcreteStrategy object & uses it to perform some business logic.

Note : The Strategy pattern is useful when you have multiple algorithms for a specific task & you want to select an algorithm at runtime. It's also useful when you have many similar classes that only differ in the way they execute some behavior.

Template Method Pattern

The Template Method pattern is a behavioral design pattern that defines the skeleton of an algorithm in the superclass but lets subclasses override specific steps of the algorithm without changing its structure.

The pattern has two main components:

  • Abstract Class - Defines abstract primitive operations that concrete subclasses define to implement steps of an algorithm. Implements a template method which defines the skeleton of an algorithm. The template method calls primitive operations as well as operations defined in AbstractClass or those of other objects.
     
  • Concrete Class - Implements the primitive operations to carry out subclass-specific steps of the algorithm

Example

class AbstractClass {
protected:
    void BaseOperation1() const {
        std::cout << "AbstractClass says: I am doing the bulk of the work." << std::endl;
    }
    void BaseOperation2() const {
        std::cout << "AbstractClass says: But I let subclasses override some operations." << std::endl;
    }
    void BaseOperation3() const {
        std::cout << "AbstractClass says: But I am doing the bulk of the work anyway." << std::endl;
    }
    virtual void RequiredOperations1() const = 0;
    virtual void RequiredOperation2() const = 0;
    virtual void Hook1() const {}
    virtual void Hook2() const {}
public:
    void TemplateMethod() const {
        BaseOperation1();
        RequiredOperations1();
        BaseOperation2();
        Hook1();
        RequiredOperation2();
        BaseOperation3();
        Hook2();
    }
};
class ConcreteClass1 : public AbstractClass {
protected:
    void RequiredOperations1() const override {
        std::cout << "ConcreteClass1 says: Implemented Operation1." << std::endl;
    }
    void RequiredOperation2() const override {
        std::cout << "ConcreteClass1 says: Implemented Operation2." << std::endl;
    }
};
class ConcreteClass2 : public AbstractClass {
protected:
    void RequiredOperations1() const override {
        std::cout << "ConcreteClass2 says: Implemented Operation1." << std::endl;
    }
    void RequiredOperation2() const override {
        std::cout << "ConcreteClass2 says: Implemented Operation2." << std::endl;
    }
    void Hook1() const override {
        std::cout << "ConcreteClass2 says: Overridden Hook1." << std::endl;
    }
};

In this example, the AbstractClass defines the template method & the common steps of the algorithm. The ConcreteClass1 & ConcreteClass2 classes implement the abstract steps defined in AbstractClass.

Note : The Template Method pattern is useful when you want to let clients extend only particular steps of an algorithm, but not the whole algorithm or its structure. It's also useful when you have several classes that contain almost identical algorithms with some minor differences. In such a case, you can move the algorithm steps into the superclass to eliminate code duplication.

Frequently Asked Questions

What are design patterns & why are they important in C++?

Design patterns are standardized solutions to common software design problems. In C++, they help manage object creation, code structure, & behavior, making the code more efficient & maintainable.

Can design patterns be mixed in a single application?

Yes, multiple design patterns can be used in a single application to solve different problems, often improving the software's overall architecture and maintainability.

How do I choose the right design pattern for my project?

Choosing the right pattern depends on the specific problems you're facing in your project. Analyze the requirements & challenges, and match them with the pattern that best addresses the issues.

Conclusion

In this article, we have learned about various design patterns in C++, including creational, structural, & behavioral patterns. We discussed each pattern in detail, containing their purpose, structure with proper code examples to show their implementation. Design patterns are powerful tools that can help you create more flexible, maintainable, & efficient code. 

You can refer to our guided paths on the Coding Ninjas. You can check our course to learn more about DSADBMSCompetitive ProgrammingPythonJavaJavaScript, etc. Also, check out some of the Guided Paths on topics such as Data Structure andAlgorithmsCompetitive ProgrammingOperating SystemsComputer Networks, DBMSSystem Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry.

Previous article
Default Constructor in C++
Next article
Type Conversion in C++
Live masterclass