Lets learn "Factory design pattern"

 In this tutorial, we'll learn about the factory design pattern as in what it is, in which use cases this is being used and how it works. If you've are not familiar with what are software design patterns and what are its different types check out this tutorial.

Introduction

A factory design pattern is a creational design pattern that is related to object creation. In this pattern, we'll have a method to create an object without exposing its creation logic to the client. If a class method is creating an object it has to be static. 
The idea is to create a static method ( factory method ) which instantiates a class without exposing its logic to the client and the client can use that method every time it needs to create an object of the class.

The reason why the factory pattern is useful is that as it does not expose the logic of object creation, so if logic is changed, the client code need not to be changed.

Usecases

The place where this pattern is mostly used is library code. Suppose in your codebase you've pulled some open source libraries from git which can change over time and you are using this library a lot to create the object of its modules. Now to ensure that our code doesn't break if the open-source library changes, library should use a Factory pattern. It should have factory methods for instantiating its modules that clients (your code) can use. With whatever changes are made in the logic of the library code, the client won't be affected.

Examples


// C++ program to demonstrate factory method design pattern 
#include <iostream> 
using namespace std; 
    
// Library classes 
class Database { 
public: 
    virtual void printDB() = 0; 
}; 
class Mongo : public Database { 
public: 
    void printDB() { 
        cout << "I am Mongo DB" << endl; 
    } 
}; 
class MySql : public Database { 
public: 
    void printDB() { 
        cout << "I am MySql DB" << endl; 
    } 
}; 
class Hbase : public Database { 
    public: 
    void printDB() { 
        cout << "I am Hbase DB" << endl; 
    } 
}; 
  
// Factory method to create objects of different types. 
// Change is required only in this function to create a new object type 
Database* Database::Create(DBType type) { 
    if (type == DBT_Mongo) 
        return new Mongo(); 
    else if (type == DBT_MySql) 
        return new MySql(); 
    else if (type == DBT_Hbase) 
        return new Hbase(); 
    else return NULL; 
} 
  
// Client class 
class Client { 
public: 
  
    // Client doesn't explicitly create objects 
    // but passes type to factory method "Create()" 
    Client(int type) 
    { 
        if(type == 1){
          db = new Mongo();
        } else if(type == 2) {
          db = new MySql();
        } else if(type == 3) {
          db = new Hbase();
        }
    } 
    ~Client() { 
        if (db) { 
            delete[] db; 
            db = NULL; 
        } 
    } 
    Database* getDB()  { 
        return db; 
    } 
  
private: 
    Database *db; 
}; 
  
// Driver program 
int main() { 
    Client *client = new Client(1); 
    Database *db = client->getDB(); 
    db->printDB(); 
    return 0; 
} 

What is the problem with the above approach?

In the above code, client code is deciding the type of database based on the type int value. What if a new database type support is added in the backend. In that case, the client code needs to be updated to handle that as well, and the client code modification is not preferred if library code changes. Let's see how we can handle this with a factory method.

// C++ program to demonstrate factory method design pattern 
#include <iostream> 
using namespace std; 
  
enum DBType { 
    DBT_Mongo,    DBT_MySql,    DBT_HBase 
}; 
  
// Library classes 
class Database { 
public: 
    virtual void printDB() = 0; 
    static Database* Create(DBType type); 
}; 
class Mongo : public Database { 
public: 
    void printDB() { 
        cout << "I am Mongo DB" << endl; 
    } 
}; 
class MySql : public Database { 
public: 
    void printDB() { 
        cout << "I am MySql DB" << endl; 
    } 
}; 
class Hbase : public Database { 
    public: 
    void printDB() { 
        cout << "I am Hbase DB" << endl; 
    } 
}; 
  
// Factory method to create objects of different types. 
// Change is required only in this function to create a new object type 
Database* Database::Create(DBType type) { 
    if (type == DBT_Mongo) 
        return new Mongo(); 
    else if (type == DBT_MySql) 
        return new MySql(); 
    else if (type == DBT_Hbase) 
        return new Hbase(); 
    else return NULL; 
} 
  
// Client class 
class Client { 
public: 
  
    // Client doesn't explicitly create objects 
    // but passes type to factory method "Create()" 
    Client() 
    { 
        Database type = DBT_Hbase; 
        db = Database::Create(type); 
    } 
    ~Client() { 
        if (db) { 
            delete[] db; 
            db = NULL; 
        } 
    } 
    Database* getDB()  { 
        return db; 
    } 
  
private: 
    Database *db; 
}; 
  
// Driver program 
int main() { 
    Client *client = new Client(); 
    Database *db = client->getDB(); 
    db->printDB(); 
    return 0; 
} 

So the above code looks great. If a new DB type is added to the library, we need not modify the client code. And so the client and the library are completely decoupled.

HOPE YOU LIKE THIS TUTORIAL. FEEL FREE TO COMMENT BELOW IF YOU HAVE ANY DOUBTS. AND STAY TUNED FOR MORE TUTORIALS :)


Comments

Popular posts from this blog

Lets learn "About kube proxy in iptables mode"

Lets learn "System design for paste bin (or any text sharing website)"