Factory Design Pattern

Sarfraz Ahmed    May 15, 2015 12:29 AM

Overview

Factory design pattern works like a factory in the real world in that it creates something for others to use. In the context of OOP, it helps in creating and instantiating objects.

Example

Let's say for our application we are using different types of databases based on requirement such as MySQL, PostgreSQL, and SQLite. Our classes look like this:

MySQL:

class MySQLDB
{
    public function setHost($host)
    {
        // code
    }
    public function setDB($db)
    {
        // code
    }
    public function setUserName($user)
    {
        // code
    }
    public function setPassword($pwd)
    {
        // code
    }
    public function connect()
    {
        // code
    }
}

PostgreSQL:

class PostgreSQLDB
{
    public function setHost($host)
    {
        // code
    }
    public function setDB($db)
    {
        // code
    }
    public function setUserName($user)
    {
        // code
    }
    public function setPassword($pwd)
    {
        // code
    }
    public function connect()
    {
        // code
    }
}

...and so on for SQLite database.

Then we instantiate different objects based on configuration:

if (Config::item('db_type') === 'mysql') {
    $DB = new MySQLDB();
    $DB->setHost("host");
    $DB->setDB("db");
    $DB->setUserName("user");
    $DB->setPassword("pwd");
    $DB->connect();
}
elseif (Config::item('db_type') === 'postgre') {
    $DB = new PostgreSQLDB();
    $DB->setHost("host");
    $DB->setDB("db");
    $DB->setUserName("user");
    $DB->setPassword("pwd");
    $DB->connect();
}
elseif (Config::item('db_type') === 'sqlite') {
    $DB = new SQLiteDB();
    $DB->setHost("host");
    $DB->setDB("db");
    $DB->setUserName("user");
    $DB->setPassword("pwd");
    $DB->connect();
}

Generally above works but it creates problems in rather big applications. Above code has these problems:

  • Should you add more database types, code would keep on growing making it complex
  • The code is hard coded with database type names
  • Whole process becomes tedious

To solve these issues, we can instead create a central factory object that would create those objects for us:

class DBFactory
{
    protected $driver = null;

    public function setDriver($driver)
    {
        $this->driver = $driver;
    }

    public function makeDB($host, $user, $pass, $dbname)
    {
        if ($this->driver === 'mysql') {
            $DB = new MySQLDB();
        }
        elseif ($this->driver === 'postgre') {
            $DB = new PostgreSQLDB();
        }
        elseif ($this->driver === 'sqlite') {
            $DB = new SQLiteDB();
        }

        $DB->setHost($host);
        $DB->setDB($dbname);
        $DB->setUserName($user);
        $DB->setPassword($pass);
        $DB->connect();

        return $DB;
    }
}

And then using it:

$dbFactory = new DBFactory;
$dbFactory->setDriver(Config::item('db_type'));
$DB = $dbFactory->makeDB("host", "db", "user", "pwd");

And that's it. You won't have to modify above code, it will always remain same, we have moved the complex object creation logic separate in the factory itself which now makes our code a lot more easier to work with.

Please notice that there are some variations of factory design pattern such as simple factory, abstract factory and factory method. In this example, we used the factory method eg we used a method named makeDB() to create our objects. Other variations are almost similar; for example with abstract factory, you actually create an abstract class and then all concrete classes should extend it. It just enforces commonality between similar concrete classes.

Note: In modern day, the object creation has become very simple with the help of Dependency Injection Containers and Service Locators. There are quite some DICs to choose from for PHP.







Comments powered by Disqus