Factory Design Pattern

This is one of the most widely used design patterns, and a really popular question in interviews.

Using a really simple example, we’re going to understand why exactly this pattern is useful and how to implement it in code.
In the Factory pattern, we create objects without exposing the creation logic to the client and the client uses the same common interface to create a new type of object.

Problem Statement:

The client has requested that we create a Vehicle Library. The vehicle can be a car, a bus, or anything else. The library should return a vehicle whenever a client requests it.

Basic Approach:

Looking at the problem, the first approach that comes to our mind might be to use OOPS, and have vehicle classes inherit from the base class. Let’s give this a try and understand why this might not be the best option for our client.

  • We start by creating the base class Vehicle, inside which we will create a pure virtual function, createVehicle(). Now even though we’re using C++ specifically in this example, this entire concept we’re learning is language independent and we’ll explore that in more detail soon.
#ifndef vehicle_hpp 
#define vehicle_hpp 
class Vehicle { 
    public: 
        virtual void createVehicle() = 0; 
}; 
#endif

In C++, a pure virtual function is one for which no function definition is required and only its declaration is required. Any class that inherits from the Vehicle Interface (class Car, class Bike etc) will now have to implement the createVehicle() function.

  • Here’s what our Car and Bike classes look like when we inherit from Vehicle, with implementations for createVehicle().
car.hpp
#ifndef car_hpp
#define car_hpp

#include "vehicle.hpp"
class Car: public Vehicle 
{
public:
    void createVehicle();
};

#endif
car.cpp
#include "car.hpp"
#include <iostream>
using namespace std;

void Car::createVehicle() 
{
   cout << "Creating Car" << endl;
}
bike.hpp
#ifndef bike_hpp
#define bike_hpp

#include "vehicle.hpp"
class Bike: public Vehicle 
{
public:
    void createVehicle();
};

#endif
bike.cpp
#include "bike.hpp"
#include <iostream>
using namespace std;

void Bike::createVehicle()
{
    cout << "Creating Bike" << endl;
}

You might have noticed that we’ve written the .hpp files along with our .cpp files, and of course we’ve already taken care of the header tags. All we’re setting up in our .cpp files is the createVehicle() function along with a print statement, keeping things simple for our example.

  • Let’s now compile our code to get object files.
g++ -c vehicle.cpp car.cpp bike.cpp
  • We’ll go ahead and name our library vehicle_library.a.
ar ru vehicle_library.a car.o bike.o
  • Finally, we’ll write the client code. All it has to do is take the user’s input of ‘vehicle type’. If the vehicle type is a “car”, a Car object is created; otherwise, the Bike object is created. Then, we need to call the function createVehicle() as well.
client.cpp
#include <iostream>
#include "car.hpp"
#include "bike.hpp"
using namespace std;

int main() 
{
    string vehicleType;
    cin>>vehicleType;
    Vehicle* vehicle;
    if(vehicleType == "car") {
        vehicle = new Car();
    }
    else if(vehicleType == "bike"){
        vehicle = new Bike();
    }
    vehicle->createVehicle();
    return 0;
}
  • Next, we compile our client code and use the Vehicle Library to create vehicles for our client.
g++ -o client client.cpp vehicle_library.a

Now when we run ./client, depending on whether we enter bike or car, we receive the corresponding vehicle, indicated by the instantiation messages “Creating Car” and “Creating Bike”.

Awesome! We have successfully implemented the Vehicle Library, giving us a Car or a Bike depending on the user input.

But is this the best approach for our client?

Definitely not! There are certain issues with this implementation, which we will now analyse.

Why is Factory the superior solution?

So far, our library only includes the Car and Bike types, but in the future, if we want to expand the functionality, we can add a Tempo or Bus. So, if we add these required files to the library,

  • We must ask the client to include the header file and the respective if else conditions in the client code.
  • As a result, every time we make changes, the client must compile the code again using the new Vehicle Library to get the new executable Client. 

This is very tightly coupled – our client should be unconcerned about what we are doing with our library.

This is where the Factory Pattern comes in to save the day!

So now we’re going to make a factory that will handle the entire creation logic without exposing it to the client, so that the client won’t be troubled. The client will only engage with the Vehicle class, unbothered about the specifics of the implementation.
The client and the creation logic are now loosely coupled.

Factory Design Pattern Implementation

  • Let’s create a new file vehicle_factory.hpp that will contain all the vehicle type headers, such as car.hpp and bike.hpp, and whenever we want to extend the vehicle types, we will simply add in the corresponding headers in this file.
  • In this file, let’s create a class called VehicleFactory and write a static function called getVehicle(). This function takes user input and returns the vehicle type. You might be wondering why we’re using a static function here. The advantage of using a static function is that we can access the function without using an object of the class. Otherwise, you can create a VehicleFactory object and then access the function but that’s unnecessary since we’re only interested in the getVehicle() function.
vehicle_factory.hpp
#ifhdef vehicle_factory_hpp
#define vehicle_factory_hpp

#include <iostream>
#include "car.hpp"
#include "bike.hpp"
using namespace std;

class VehicleFactory {
    public:
         static Vehicle* getVehicle(string vehicleType);
}
#endif
  • Now for our vehicle_factory.cpp file. In here, we’ll implement our getVehicle() function and the if-else logic that was previously present in the client code will be copied to this file. So every time we want to introduce a new vehicle type or make changes, those changes will be made in this file.
vehicle_factory.cpp
#include "vehicle_factory.hpp"

Vehicle* VehicleFactory::getVehicle(string vehicleType) 
{
    Vehicle* vehicle;
    if (vehicleType == "car")
    {
        vehicle = new Car();
    }
    else if (vehicleType == "bike")
    {
        vehicle = new Bike();
    }
    return vehicle;
}
  • And finally, a smart_client.cpp file that’s going to use the factory without any of the if-else logic inside it. All the client needs to do is take the user input, vehicleType, and use the VehicleFactory to create the Vehicle object by using the getVehicle() function. And using the Vehicle object, we call the createVehicle() function to create the object of the specific vehicleType.
smart_client.cpp
#include "vehicle_factory.hpp"
#include <iostream>
using namespace std;

int main() {
    string vehicleType;
    cin >> vehicleType;
    Vehicle *vehicle = VehicleFactory::getVehicle(vehicleType);
    vehicle->createVehicle();
    return 0;
}

The client will be overjoyed now since they simply have to feed the input and use the factory to produce and get the required vehicle.

Let’s run a demo and see how it all comes together!

In compiling the code, we need to make sure to add the vehicle_factory.o object file in the Vehicle Library.

g++ -c vehicle_factory.cpp car.cpp bike.cpp vehicle.cpp
ar ru vehicle_library.a vehicle_factory.o car.o bike.o
g++ -o smartClient smart_client.cpp vehicle_library.a

Conclusion

To summarise,

  • The library should be responsible for deciding which object type to create based on the user input.
  • The Client should just call the library’s factory and pass the vehicle type without worrying about the actual implementation of object creation.

Our client now simply uses the factory to create the product they want. All the components of this process are now decoupled.

The factory pattern is thus a very useful tool in a situation where we have on superclass and multiple sub-classes, one of which we need to instantiate.

Would you like to watch a video walking you through this Factory Pattern implementation? Check out our video on the official YouTube channel!

Was This Helpful?

Scan to Pay With Your Favourite Upi App
to Buy me a Coffee!

keerti upi qr code