The blog post introduces creational design patterns in C++.

Design Patterns
In C++, we have countless options for setting up our classes and objects to solve particular problems in software development. However, this freedom to choose any solution can lead to writing problem-specific code with poorly designed constructors, destructors, inheritance, and composition. Such code often results in reduced readability, maintainability, and scalability.
To address these challenges, software developers have created design patterns—standardized ways of solving common software problems. These patterns help developers write scalable and easily understandable code that others can work with effectively. There are three main types of design patterns: creational, structural, and behavioral design patterns, each serving different purposes. In this article, we will introduce you to some creational design patterns, which aim to make object creation more flexible and efficient.
Factory Method Pattern
The Factory Method Pattern involves creating a IFactory
interface that different factory classes can implement.
This approach allows all the factory classes to create objects of different classes in a consistent manner.
Below is an example of using the Factory Method Pattern to create objects for two different shapes:
// Abstract Class for Shapes
class IShape {
public:
virtual void draw() = 0;
virtual ~Shape(){};
};
// Abstract Class for Factories
class IShapeFactory {
public:
virtual Shape* createShape() = 0;
virtual ~ShapeFactory() {};
};
// Shape Circle
class Circle : public IShape {
public:
void draw() override
{
std::cout<<"Drawing a Circle"<<std::endl;
}
};
// Shape Square
class Square : public IShape {
public:
void draw() override
{
std::cout<<"Drawing a Square"<<std::endl;
}
};
// Circle Factory
class CircleFactory : public IShapeFactory {
public:
Shape* createShape() override { return new Circle(); }
};
// Square Factory
class SquareFactory : public IShapeFactory {
public:
Shape* createShape() override { return new Square(); }
};
int main() {
ShapeFactory* circleFactory = new CircleFactory();
ShapeFactory* squareFactory = new SquareFactory();
Shape* circle = circleFactory->createShape();
Shape* square = squareFactory->createShape();
circle->draw(); // => Drawing a Circle
square->draw(); // => Drawing a Square
delete circleFactory;
delete squareFactory;
delete circle;
delete square;
return 0;
}
By using the IShapeFactory
interface, both CircleFactory
and SquareFactory
can implement the createShape
method to create Circle
and Square
objects, respectively.
While the benefits might not be immediately apparent in this example, the Factory Method Pattern is particularly useful in cases where creating an object of a parent class requires multiple child class objects.
In such scenarios, the parent object creation logic can be encapsulated within the factory. Additionally, using interfaces like IShapeFactory
ensures that adding a new class derived from IShape
doesn’t affect other parts of the code.
It only involves creating a new concrete factory class derived from IShapeFactory
.
Builder Pattern
The Builder Pattern involves creating a Builder
class to build an object step-by-step by processing configuration parameters one at a time.
This approach simplifies the creation of objects that require many parameters. Here’s an example of how the Builder Pattern can be utilized:
// Pizza class to build
class Pizza {
public:
string size;
string sauce;
string cheese;
string toppings;
public:
void setSize(string size) {
this->size=size;
};
void setSauce(string sauce) {
this->sauce=sauce;
};
void setCheese(string cheese) {
this->cheese=cheese;
};
void setToppings(string toppings) {
this->toppings=toppings;
};
void print() {
cout << "Size: " << this->size << endl;
cout << "Sauce: " << this->sauce << endl;
cout << "cheese: " << this->cheese << endl;
cout << "Toppings: " << this->toppings << endl;
}
};
// Builder for Pizza
class PizzaBuilder {
public:
Pizza pizza;
public:
PizzaBuilder &addSize(string size) {
this->pizza.setSize(size);
return *this;
};
PizzaBuilder &addSauce(string sauce) {
this->pizza.setSauce(sauce);
return *this;
};
PizzaBuilder &addCheese(string cheese) {
this->pizza.setCheese(cheese);
return *this;
};
PizzaBuilder &addToppings(string toppings) {
this->pizza.setToppings(toppings);
return *this;
};
Pizza &build() {
return this -> pizza;
};
};
int main () {
PizzaBuilder builder;
builder.addSize("Medium").addSauce("Tomato");
Pizza pizza = builder.addCheese("Cheddar").addToppings("Pepperoni").build();
pizza.print();
return 0;
};
The builder uses the setter functions of the class to build and configure each attribute, enabling method chaining. More complex logic can also be implemented within each method of the builder if needed. Although the benefits of the Builder Pattern are intuitive, it can sometimes lead to redundant code with numerous simple setter functions and builder methods.
Singleton
There are scenarios where we want to ensure that only one instance of a class is created and shared across the application’s runtime (daemon processes for networking and database). In such cases, the Singleton Pattern can be used.
class Singleton {
private:
static Singleton* instance;
private:
Singleton() {
cout << "Singleton instance created." << endl;
};
~Singleton() {
cout << "Singleton instance destroyed."<< endl;
};
public:
static Singleton& getInstance()
{
// If the instance doesn't exist, create it
if (!instance) {
instance = new Singleton();
}
return *instance;
};
void someOperations() {
cout << "Singleton is performing some operation." << endl;
};
};
// Initialize the static instance variable to nullptr
Singleton* Singleton::instance = nullptr;
int main()
{
// Access the Singleton instance
Singleton& singleton = Singleton::getInstance();
// Use the Singleton instance
singleton.someOperations();
// Attempting to create another instance will not work
// Singleton anotherInstance; // This line would not compile
return 0;
}
This pattern leverages static
attributes and methods, which are global variables and shared across all instances and functions that access them.
In the getInstance
method above, if there is no existing instance of the Singleton
class, a new instance is created. Subsequent calls to the method return
the same instance. Since the constructor is private and can only be accessed by getInstance
, it is made to be impossible to create
another instance of Singleton
.
Conclusion
In this article, we discussed the concept of design patterns, their usefulness, and examples of creational design patterns. In the next two articles, we will explore examples of structural and behavioral design patterns, which are invaluable for writing and understanding the code of large projects in any object-oriented programming language.
Resources
- GeeksforGeeks. 2024. Builder Pattern | C++ Design Patterns. GeeksforGeeks.
- GeeksforGeeks. 2024. Factory Method Pattern | C++ Design Patterns. GeeksforGeeks.
- GeeksforGeeks. 2023. Singleton Pattern | C++ Design Patterns. GeeksforGeeks.