This complete tutorial on C++ Design Patterns (Basics) explains commonly used patterns including Singleton, Factory, Observer, and Strategy. It helps learners understand object-oriented design, reusability, and maintainable code following best practices.
Design Patterns (Basics) – Complete Tutorial
1. What are Design Patterns?
Design patterns are reusable solutions to common software problems.
- Help with code maintainability, readability, and scalability
- Categorized as:
- Creational – object creation (Singleton, Factory)
- Structural – object composition (Decorator, Adapter)
- Behavioral – communication between objects (Observer, Strategy)
2. Singleton Pattern
Ensures a class has only one instance and provides a global access point.
Example:
#include <iostream>
using namespace std;
class Singleton {
private:
static Singleton* instance;
Singleton() {}
public:
static Singleton* getInstance() {
if(!instance) instance = new Singleton();
return instance;
}
void show() { cout << "Singleton Instance\n"; }
};
Singleton* Singleton::instance = nullptr;
int main() {
Singleton* s1 = Singleton::getInstance();
Singleton* s2 = Singleton::getInstance();
s1->show();
cout << (s1==s2); // true
}
Best Practices:
- Prevent copying with
deletecopy constructor - Thread-safe implementation if needed
3. Factory Pattern
Provides an interface to create objects without exposing the creation logic.
Example:
#include <iostream>
using namespace std;
class Shape {
public:
virtual void draw() = 0;
};
class Circle : public Shape {
void draw() { cout << "Drawing Circle\n"; }
};
class Square : public Shape {
void draw() { cout << "Drawing Square\n"; }
};
class ShapeFactory {
public:
static Shape* createShape(string type) {
if(type=="circle") return new Circle();
if(type=="square") return new Square();
return nullptr;
}
};
int main() {
Shape* s1 = ShapeFactory::createShape("circle");
Shape* s2 = ShapeFactory::createShape("square");
s1->draw();
s2->draw();
}
Use Cases:
- Object creation abstraction
- Simplify client code
4. Observer Pattern
Defines a one-to-many dependency so that when one object changes, all dependents are notified.
Example:
#include <iostream>
#include <vector>
using namespace std;
class Observer {
public:
virtual void update(int value) = 0;
};
class ConcreteObserver : public Observer {
void update(int value) { cout << "Observer notified: " << value << endl; }
};
class Subject {
vector<Observer*> observers;
int state;
public:
void attach(Observer* obs) { observers.push_back(obs); }
void setState(int s) {
state = s;
for(auto obs : observers) obs->update(state);
}
};
int main() {
Subject subject;
ConcreteObserver o1, o2;
subject.attach(&o1);
subject.attach(&o2);
subject.setState(100);
}
Use Cases:
- Event handling
- Model-View-Controller (MVC)
5. Strategy Pattern
Encapsulates algorithms in separate classes and makes them interchangeable at runtime.
Example:
#include <iostream>
using namespace std;
class Strategy {
public:
virtual void execute() = 0;
};
class ConcreteStrategyA : public Strategy {
void execute() { cout << "Executing Strategy A\n"; }
};
class ConcreteStrategyB : public Strategy {
void execute() { cout << "Executing Strategy B\n"; }
};
class Context {
Strategy* strategy;
public:
Context(Strategy* s) : strategy(s) {}
void setStrategy(Strategy* s) { strategy = s; }
void executeStrategy() { strategy->execute(); }
};
int main() {
ConcreteStrategyA sA;
ConcreteStrategyB sB;
Context context(&sA);
context.executeStrategy();
context.setStrategy(&sB);
context.executeStrategy();
}
Use Cases:
- Dynamic behavior selection
- Encapsulate algorithms
Best Practices
- Prefer interfaces/abstract classes for flexibility
- Keep single responsibility for each class
- Use patterns to solve common problems, not over-engineer
- Document pattern usage for team understanding
Common Mistakes
- Overusing patterns where simple code works
- Mixing multiple patterns without clear design
- Ignoring thread-safety for singleton or shared resources
Summary
In this chapter, you learned Design Patterns (Basics) in C++, including:
- Singleton (creational)
- Factory (creational)
- Observer (behavioral)
- Strategy (behavioral)
Understanding these patterns improves code reusability, maintainability, and scalability in real-world C++ applications.