factory design pattern

Article directory

  • 1. Simple Factory Pattern & Static Factory Pattern
  • 2. Factory method pattern
  • 3. Abstract Factory Pattern

Illustrations from: https://refactoringguru.cn/design-patterns/catalog

1. Simple factory pattern & amp; static factory pattern

Simple Factory Pattern Strictly speaking, it is not a design pattern, but more like a programming habit. The simple factory pattern does not expose the creation logic to the client when creating objects, but provides newly created objects by using a common interface.

When the provided object interface is defined as a static method, the simple factory pattern is also called Static Factory Pattern.

The simple factory pattern includes the following roles:

  • Abstract product: Defines product specifications, describes the main features and functions of the product, and is the base class of all specific products.
  • Concrete product: Implement or inherit abstract products.
  • Concrete factory: Provides a method for creating products, through which the caller obtains the product.

Advantages of simple factory pattern:

  • Encapsulates the process of creating an object: When a caller wants to create an object, he only needs to know its name.
  • Object creation and business logic are separated: If you want to add a new product, you only need to modify the factory class instead of modifying the original code, which reduces the possibility of customer code modification and increases the code to a certain extent. scalability.

Disadvantages of the simple factory model: When adding new products, you need to modify the code of the factory class, which violates the open-closed principle (open to extensions, closed to modifications).

/* Automotive interface */
class Car {<!-- -->
public:
    virtual void drive() = 0;

    virtual ~Car() = default;
};

/* car */
class Sedan : public Car {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Sedan is driving." << endl;
    }
};

/* Truck */
class Truck : public Car {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Truck is driving." << endl;
    }
};

/* Static factory class */
class CarFactory {<!-- -->
public:
    static Car *createCar(const string & amp;carType) {<!-- -->
        if (carType == "Sedan") {<!-- -->
            return new Sedan();
        } else if (carType == "Truck") {<!-- -->
            return new Truck();
        } else {<!-- -->
            return nullptr;
        }
    }
};

int main() {<!-- -->
    unique_ptr<Car> sedan(CarFactory::createCar("Sedan"));
    unique_ptr<Car> truck(CarFactory::createCar("Truck"));

    sedan->drive();
    truck->drive();

    return 0;
}

2. Factory method pattern

Factory method pattern is a creational design pattern that provides a method interface for creating objects in the parent class, allowing subclasses to determine the method of the object to be instantiated. Type. The factory method pattern defers the instantiation of a product class to a subclass of the factory.

The factory method pattern includes the following roles:

  • Abstract product: Defines product specifications, describes the main features and functions of the product, and is the base class of all specific products.
  • Concrete product: Implements or inherits abstract products, which are created by concrete factories and have a one-to-one correspondence with concrete factories.
  • Abstract Factory: Provides an interface for creating products. The caller can use the factory method to access the specific factory to create the product.
  • Concrete factory: It mainly implements the abstract methods in the abstract factory and completes the creation of specific products.

Advantages of factory method pattern:

  • Users only need to know the name of the specific factory to get the product they want, without knowing the specific creation process of the product, thus avoiding tight coupling between users and specific products.
  • Product creation code can be placed in a single location in the program, making the code easier to maintain and satisfying the single responsibility principle.
  • When adding new products to the system, you only need to add specific product categories and corresponding specific factory categories. There is no need to make any modifications to the original factory, meeting the opening and closing principle.

Disadvantages of the factory method pattern: Every time a product is added, a specific product class and a corresponding specific factory class are added, which increases the complexity of the system.

/* Automotive interface */
class Car {<!-- -->
public:
    virtual void drive() = 0;

    virtual ~Car() = default;
};

/* Factory interface */
class CarFactory {<!-- -->
public:
    virtual Car *createCar() = 0;

    virtual ~CarFactory() = default;
};

/* car */
class Sedan : public Car {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Sedan is driving." << endl;
    }
};

/* Truck */
class Truck : public Car {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Truck is driving." << endl;
    }
};

/* Car factory */
class SedanFactory : public CarFactory {<!-- -->
public:
    Car *createCar() override {<!-- -->
        return new Sedan();
    }
};

/* Truck Factory */
class TruckFactory : public CarFactory {<!-- -->
public:
    Car *createCar() override {<!-- -->
        return new Truck();
    }
};

int main() {<!-- -->
    unique_ptr<CarFactory> sedan_factory(new SedanFactory());
    unique_ptr<CarFactory> truck_factory(new TruckFactory());

    unique_ptr<Car> sedan(sedan_factory->createCar());
    unique_ptr<Car> truck(truck_factory->createCar());

    sedan->drive();
    truck->drive();
    
    return 0;
}

3. Abstract factory pattern

Abstract Factory Pattern is an upgraded version of Factory Method Pattern. Factory Method Pattern only produces a single product, while Abstract Factory Pattern can produce a series of related products.

The abstract factory pattern includes the following roles:

  • Abstract product: Defines product specifications, describes the main features and functions of the product, and is the base class of all specific products.
  • Concrete product: Implements or inherits an abstract product, which is created by a concrete factory. It has a many-to-one relationship with the concrete factory.
  • Abstract Factory: Provides an interface for creating products. The caller can use the factory method to access the specific factory to create a series of related products.
  • Concrete factory: It mainly implements the abstract methods in the abstract factory and completes the creation of specific products.

Advantages of abstract factory method pattern:

  • You can ensure that products produced in the same factory match each other, for example the Ford factory in the example below does not produce Volvo cars.
  • Coupling of client and product-specific code can be avoided.
  • Improved based on the factory method model, it also meets the single responsibility principle and the open-closed principle.

Disadvantages of the abstract factory method pattern: The code is more complex. At the same time, similar to the factory method, every time a new product needs to be added to the product family, all factory classes need to be modified. For example, if we add a method about commercial vehicles in the following code, all factory classes including abstract factories need to be modified.

/* Car interface */
class Sedan {<!-- -->
public:
    virtual void drive() = 0;

    virtual ~Sedan() = default;
};

/* Truck interface */
class Truck {<!-- -->
public:
    virtual void drive() = 0;

    virtual ~Truck() = default;
};

/* Factory interface */
class CarFactory {<!-- -->
public:
    virtual Sedan *createSedan() = 0;

    virtual Truck *createTruck() = 0;

    virtual ~CarFactory() = default;
};

/* Ford sedan */
class FordSedan : public Sedan {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Ford sedan is driving." << endl;
    }
};

/* Ford Truck */
class FordTruck : public Truck {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Ford Truck is driving." << endl;
    }
};

/* Volvo sedan */
class VolvoSedan : public Sedan {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Volvo sedan is driving." << endl;
    }
};

/* Volvo Trucks */
class VolvoTruck : public Truck {<!-- -->
public:
    void drive() override {<!-- -->
        cout << "Volvo Truck is driving." << endl;
    }
};

/* Ford factory */
class FordFactory : public CarFactory {<!-- -->
public:
    Sedan *createSedan() override {<!-- -->
        return new FordSedan();
    }

    Truck *createTruck() override {<!-- -->
        return new FordTruck();
    }
};

/* Volvo factory */
class VolvoFactory : public CarFactory {<!-- -->
public:
    Sedan *createSedan() override {<!-- -->
        return new VolvoSedan();
    }

    Truck *createTruck() override {<!-- -->
        return new VolvoTruck();
    }
};

int main() {<!-- -->
    unique_ptr<CarFactory> ford_factory = make_unique<FordFactory>();
    unique_ptr<CarFactory> volvo_factory = make_unique<VolvoFactory>();

    unique_ptr<Sedan> ford_sedan(ford_factory->createSedan());
    unique_ptr<Truck> ford_truck(ford_factory->createTruck());
    unique_ptr<Sedan> volvo_sedan(ford_factory->createSedan());
    unique_ptr<Truck> volvo_truck(ford_factory->createTruck());

    ford_sedan->drive();
    ford_truck->drive();
    volvo_sedan->drive();
    volvo_truck->drive();

    return 0;
}