Class Factory Pattern (C++)


What exactly is class factory?

Let’s say you were to build a car dealership simulation/application. You have an purely abstract base class called Car (or Auto). You have a manager class called Dealership, that manages all aspects of the facility, such as clients, car inventory, employees, and records of transactions. Let’s also assume that this car sales facility is expected to receive a shipment of exotic imported vehicles to add to the inventory.

Now let’s go back the abstract idea of the Car class. Car is an abstract class/object in that there are no specific attributes, but you can give it some universal properties such as the year, make, model, and price. And you can give this abstract object some purely virtual functions as well, such as displayInfo(), display3DImage(), and for creativity purposes, testDrive(). Now, lets derive some specific types of cars from it.

We can derive: LuxuryCar, HatchbackCar, SportCar, and UtilityCar classes from the abstract Car class. Now let’s move on…

Because we are building a software application that handles all aspects of a car dealership, we need a way to update our inventory to handle the new shipment of cars coming in. We’ve been sent a data file containing all the information of each car. The data is stored according to the type of car it is based on (such as luxury, sport, utility, etc). Entering all this information by hand into the system is a very slow and inefficient process, so let’s use a class factory pattern.

Class factory pattern is an object/method for creating other objects. Basically, we have some class or function that creates and returns a new object based on whatever data is provided. Factory pattern is a basic design principle in OOP, as an abstraction of a constructor of a class. The class factory creates and returns new objects by instantiating various classes (through the use of constructors). In most cases, a class factory has at most one method/function that is given some type of incoming data, and returns a new object (or pointer) to what was created.

In the case of the car dealership, we can pass in the data we’re given into a class factory that will create new cars such as returning a new LuxuryCar(), or returning a new SportCar(). We return a pointer of type Car, this works because all these derived classes inherit from the abstract base class. Let’s see an example in C++…

Simple UML of a Shape class factory pattern
Simple UML of a Shape class factory pattern
Another UML of a class factory, this one is based on a car class
Another UML of a class factory, this one is based on a car class

 

 

 

 

 

 

 

Implementation:

    // Car.h
    public:
    Car();
    ~virtual Car();
    
    virtual void displayInfo() const = 0;
    virtual void display3DImage() const = 0;
    virtual void testDrive() const = 0;
    private:
    protected:
    string make, model;
    int year, price;
//LuxuryCar.h
class LuxuryCar: public Car
{
    public:
    LuxuryCar();
    LuxuryCar(string model, string make, string country, int year, int price);
    ~virtual LuxuryCar();
    
    void displayInfo() const;
    void display3DImage() const;
    void testDrive() const;
    private:
    string countryOfOrigin;
    protected:
    string make, model;
    int year, price;
};
//CarFactory.h
class CarFactory
{
    //no constructor, just a static method
    public: //assume inputStream is where our data comes from
    static Car *createCar(ifstream &inputStream);
    //to call a static method, use syntax like so: CarFactory::createCar();
};
//main factory method
Car *CarFactory::createCar(ifstream &input)
{
    char type;
    string model, make, countryOfOrigin;
    int year, price, topSpeed, doors, maxCarryWeight;
    input >> type;
    switch(type):
    {
        case 'L':
        {
            input >> model >> make >> countryOfOrigin >> year >> price;
            return new LuxuryCar(model, make, countryOfOrigin, year, price);
        }
        case 'S':
        {
            input >> model >> make >> year >> price >> topSpeed;
            return new SportCar(model, make, year, price, topSpeed);
        }
        case 'H':
        {
            input >> model >> make >> year >> price >> doors;
            return new HatchbackCar(model, make, year, price, doors);
        }
        case 'U':
        {
            input >> model >> make >> year >> price >> maxCarryWeight;
            return new UtilityCar(model, make, year, price, maxCarryWeight);
        }
        default:
        {
            cout<<"Invalid data"<<endl;
            return NULL;
        }
    }
}

Uses and Advantages:

Factories can be used when:

  1. The creation of an object makes reuse impossible without significant duplication of code.
  2. The creation of an object requires access to information or resources that should not be contained within the composing class.
  3. The lifetime management of the generated objects must be centralized to ensure a consistent behavior within the application.

Factories, specifically factory methods, are common in toolkits and frameworks, where library code needs to create objects of types that may be subclassed by applications using the framework.

Using factories instead of constructors or prototypes allows one to use polymorphism for object creation, not only object use. Specifically, using factories provides encapsulation, and means the code is not tied to specific classes or objects, and thus the class hierarchy or prototypes can be changed or refactored without needing to change code that uses them – they abstract from the class hierarchy or prototypes.

Leave a comment