Abstract Factory Pattern
The Abstract Factory design pattern is like a factory of factories. It provides an interface for creating families of related objects, but it doesn’t specify the exact classes of those objects. Instead, it lets the concrete factories decide which classes to create. Each concrete factory is responsible for creating objects that belong to the same family and work together cohesively.
Imagine you are designing a game that can run on different platforms (e.g., PC, Xbox, PlayStation). Each platform requires different sets of objects, such as characters, weapons, and backgrounds. The Abstract Factory pattern helps you create these objects in a platform-specific manner without cluttering your code with platform-specific logic.
Abstract Factory C++ Example:
Let’s see example to illustrate the Abstract Factory pattern for creating shapes and colors.
We’ll have two families of objects: shapes (e.g., Circle, Square) and colors (e.g., Red, Blue).
We’ll create an abstract factory to create shapes and colors, and then two concrete factories to generate specific shapes and colors.
#include <iostream>
// Abstract Shape class
class Shape {
public:
virtual void draw() = 0;
};
// Concrete Shape classes
class Circle : public Shape {
public:
void draw() override {
std::cout << "Drawing a Circle." << std::endl;
}
};
class Square : public Shape {
public:
void draw() override {
std::cout << "Drawing a Square." << std::endl;
}
};
// Abstract Color class
class Color {
public:
virtual void fill() = 0;
};
// Concrete Color classes
class Red : public Color {
public:
void fill() override {
std::cout << "Filling with Red color." << std::endl;
}
};
class Blue : public Color {
public:
void fill() override {
std::cout << "Filling with Blue color." << std::endl;
}
};
// Abstract Factory class
class AbstractFactory {
public:
virtual Shape* createShape() = 0;
virtual Color* createColor() = 0;
};
// Concrete Factory classes for Shape family
class ShapeFactory : public AbstractFactory {
public:
Shape* createShape() override {
// Here, we create a Circle, but it could be any shape based on some logic.
return new Circle();
}
Color* createColor() override {
// Shape factory does not create colors, return nullptr
return nullptr;
}
};
// Concrete Factory classes for Color family
class ColorFactory : public AbstractFactory {
public:
Shape* createShape() override {
// Color factory does not create shapes, return nullptr
return nullptr;
}
Color* createColor() override {
// Here, we create a Red color, but it could be any color based on some logic.
return new Red();
}
};
// Client class that uses the Abstract Factory
class Client {
public:
void drawAndFill(AbstractFactory* factory) {
Shape* shape = factory->createShape();
Color* color = factory->createColor();
if (shape) {
shape->draw();
delete shape;
}
if (color) {
color->fill();
delete color;
}
}
};
int main() {
Client client;
// Using Shape factory to create a Circle and no color.
AbstractFactory* shapeFactory = new ShapeFactory();
client.drawAndFill(shapeFactory);
delete shapeFactory;
// Using Color factory to create no shape and a Red color.
AbstractFactory* colorFactory = new ColorFactory();
client.drawAndFill(colorFactory);
delete colorFactory;
return 0;
}
Explanation of the Code:
In this example, we have two families of objects: shapes (Circle, Square) and colors (Red, Blue). We define an abstract AbstractFactory
class with two pure virtual methods: createShape()
and createColor()
. These methods are responsible for creating shapes and colors, respectively.
We create two concrete factories: ShapeFactory
and ColorFactory
, each of which implements the AbstractFactory
interface. The ShapeFactory
creates shapes (here, a Circle) and returns nullptr
for colors, while the ColorFactory
creates colors (here, Red) and returns nullptr
for shapes.
The Client
class uses the Abstract Factory pattern by accepting an AbstractFactory
object as an argument to its drawAndFill()
method. The drawAndFill()
method creates a shape and a color using the provided factory, if available, and calls their respective methods (draw()
and fill()
).
In the main()
function, we demonstrate how the Abstract Factory pattern works. We create instances of ShapeFactory
and ColorFactory
, and the Client
class uses them to create and draw shapes and fill colors.
The Abstract Factory pattern allows you to create families of related objects without specifying their concrete classes. It ensures that the created objects are compatible and consistent within a family. By using the Abstract Factory pattern, you can decouple client code from specific object creation, making it easier to add new families of objects without modifying existing code.