The Factory design pattern is a popular way to delegate the creation of objects to a single factory class, based on arguments passed from client code. This helps keep your code clean and flexible, especially when you have many types of objects to create.

Advantages Link to heading

The Factory pattern makes your code easier to maintain and extend. Instead of scattering object creation logic throughout your codebase, you centralize it in one place. This means you can add new types or change how objects are created without modifying client code. It also helps with encapsulation, reduces code duplication, and makes testing easier because you can swap out implementations more easily.

Link to UVM Link to heading

UVM uses the factory via type_id::create(), which means tests can change the actual class being created. For example, instead of foobar_driver, we can use foobar_err_inject_driver to create a driver with error injection features.

Example Link to heading

The typical example is Shape and ShapeFactory.

#include <string>
#include <iostream>

using namespace std;

class Shape {
public:
    virtual void draw() = 0;
    virtual ~Shape() = default;
};

class Circle : public Shape {
public:
    void draw() override {
        cout << "Drawing a Circle" << std::endl;
    }
};

class Square : public Shape {
public:
    void draw() override {
        cout << "Drawing a Square" << std::endl;
    }
};

class ShapeFactory {
public:
    static Shape* createShape(const std::string& type) {
        if (type == "circle") {
            return new Circle();
        } else if (type == "square") {
            return new Square();
        }
        return nullptr;
    }
};

int main() {
    Shape* circle = ShapeFactory::createShape("circle");
    Shape* square = ShapeFactory::createShape("square");

    circle->draw();
    square->draw();

    delete circle;
    delete square;

    return 0;
}