C++ Design Patterns Basics | Singleton, Factory, Observer, Strategy


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.

  1. Help with code maintainability, readability, and scalability
  2. Categorized as:
  3. Creational – object creation (Singleton, Factory)
  4. Structural – object composition (Decorator, Adapter)
  5. 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:

  1. Prevent copying with delete copy constructor
  2. 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:

  1. Object creation abstraction
  2. 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:

  1. Event handling
  2. 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:

  1. Dynamic behavior selection
  2. Encapsulate algorithms

Best Practices

  1. Prefer interfaces/abstract classes for flexibility
  2. Keep single responsibility for each class
  3. Use patterns to solve common problems, not over-engineer
  4. Document pattern usage for team understanding

Common Mistakes

  1. Overusing patterns where simple code works
  2. Mixing multiple patterns without clear design
  3. Ignoring thread-safety for singleton or shared resources

Summary

In this chapter, you learned Design Patterns (Basics) in C++, including:

  1. Singleton (creational)
  2. Factory (creational)
  3. Observer (behavioral)
  4. Strategy (behavioral)

Understanding these patterns improves code reusability, maintainability, and scalability in real-world C++ applications.