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++
#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;
}

You can also try this code with Online C++ Compiler
Run Code
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++
#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;
}

You can also try this code with Online C++ Compiler
Run Code
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++
#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;
}

You can also try this code with Online C++ Compiler
Run Code
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 DSA, DBMS, Competitive Programming, Python, Java, JavaScript, etc. Also, check out some of the Guided Paths on topics such as Data Structure andAlgorithms, Competitive Programming, Operating Systems, Computer Networks, DBMS, System Design, etc., as well as some Contests, Test Series, and Interview Experiences curated by top Industry.