Patterns For PHP
Index Patterns News Forums

Abstract Factory

From Patterns For PHP

Contents

Introduction

If you have not already done so, read up on the Factory Pattern. The Abstract Factory pattern is often easy to misunderstand because its complexity in certain situations. Here we will present two examples, one simply to demonstrate the concept, and another showing a more typical in-depth use.

Consider a case where we have implemented a variety of Factories. These could be multiple Database Abstraction Factories or numerous types of Log Formatters and Log Writers (both from our Factory Pattern examples). The problem with having multiple Factories is that at some point we need to decide which Factory we are going to use. This decision can be based on some application setting, or be a consequence of some other internal logic. Assuming such logic is required every time we need to decide upon a Factory to use, we can simplify the decision making by abstracting it into a specialized class - an Abstract Factory.

Definition

Definition: An object implementing the Abstract Factory Pattern creates Factories of a specific type which in turn are used to create concrete objects of a specific type.

The definition above may vary. The use of the Abstract Factory Pattern can differ among implementations. In many cases the AbstractFactory will create a concrete Factory and control its use internally rather than simply passing the Factory back to the client. In the examples below we assume the client simply want to get hold of the actual concrete Factory object to use themselves.

A PHP 5 Example

The Abstract Factory Pattern is difficult to explain with a simple example (yet). The result of the first part of our Implementation section however would be summarized as follows.

In creating an application, a development team has created a small framework (a collection of commonly used components) to speed production. The framework requires a great deal of flexibility, including the ability to switch between two or more database abstraction libraries based on a configuration option. Each database abstraction library has a dedicated Factory class to manage its instantiation and setup details (abstraction of creational logic).

To decide which of these Factories (and hence which library) to utilize a switch statement is written. Unfortunately, as time passes, the switch statement ends up being copied to several areas of the framework. To remove this code duplication, the logic to determine which Factory to utilize is consolidated in a specialised class. This class will determine which Factory should be used and return an instance of the Factory for developers to use.

This specialised class is an Abstract Factory; it creates a concrete Factory object from a family of related Factories (database abstraction) which are then used to create database abstraction objects.

php
// instantiate Abstract Factory
$abstractfactory = new DB_Abstraction_AbstractFactory();
 
// fetch a concrete Factory (decision handled by Abstract Factory static method)
$factory = $abstractfactory::getFactory();
 
// use concrete Factory to create a database connection object from
// the selected database abstraction library
$db = $factory->createInstance();

Implementing the Abstract Factory Pattern

Let's return to our Factory Pattern example of a Database Abstraction library. Where we left off, we had created a Factory for the ADOdb Lite library which handled all the details and murky logic of creating a useable connection object to perform database operations. As we previously mentioned, some member of our team working on the application may decide they need to utilise an alternative library such as PEAR::MDB2. Obviously this is a simple matter of creating a new MDB2 Factory.

Of course things are never that simple :). Now that we are planning for two related Factory objects, we should keep in mind that duplicated code is troublesome. To avoid this we should consider adding a Parent class which will contain all source code we expect to be common to both the ADOdb Lite and MDB2 Factories.

The result of this process is a parent abstract class "DB_Abstraction_Factory" and two concrete child classes, "DB_Abstraction_Factory_ADODBLITE" and "DB_Abstraction_Factory_MDB2". Also included is an interface. The interface is optional, however in following the design pattern principle //'code to an interface, not an implementation//' we realize that all Factories should follow the same interface if we are to allow the possibility of using an Abstract Factory.

The code for the above would look similar to:

php
interface iFactory {
 
    public function createInstance();
 
}
 
abstract class DB_Abstraction_Factory {
 
    protected $settings = array();
 
    protected function __construct() {
        $this->settings = Settings::getInstance();
    }
 
    abstract public function createInstance();
 
}
 
class DB_Abstraction_Factory_ADODB extends DB_Abstraction_Factory implements iFactory {
 
    public function __construct() {
        parent::__construct();
    }
 
    public function createInstance() {
        require_once('/path/to/adodb_lite/adodb.inc.php');
        $dsn = $this->settings['db.dsn'];
        $db = ADONewConnection($dsn);
        return $db;
    }
 
}
 
class DB_Abstraction_Factory_MDB2 extends DB_Abstraction_Factory implements iFactory {
 
    public function __construct() {
        parent::__construct();
    }
 
    public function createInstance() {
        require_once 'MDB2.php';
        $dsn = $this->settings['db.dsn'];
        $db = MDB2::factory($dsn);
        return $db;
    }
 
}

These simple Factories look almost too simple, but I can assure you the logic in loading ADOdb Lite plugins can be fairly complex once you decide to use them.

With our two Factories in their neat structure with a common abstract parent class, the code in which we will use the Factories will now need to decide which Factory (and hence which library) to use. A simple switch statement will handle this nicely, since the library type will be a configuration option. However what happens when this decision needs to be made across multiple classes? We end up with a duplicated switch statement and if we decide to add a new library we will need to locate and edit every copy of the statement.

To remove this duplication and consolidate the decision, we can create a specialized class - our Abstract Factory.

php
interface iAbstractFactory {
 
    public static function getFactory();
 
}
 
class DB_Abstraction_AbstractFactory implements iAbstractFactory {
 
    private function __construct() {
	
    }
 
    public static function getFactory() {
        $settings = Settings::getInstance();
        require_once('iFactory.php');
        require_once('DB_Abstraction_Factory.php');
        switch($settings['db.library'])
        {
            case 'adodblite':
                require_once('DB_Abstraction_Factory_ADODBLITE.php');
                $factory = new DB_Abstraction_Factory_ADODBLITE();
            break;
            case 'mdb2';
                require_once('DB_Abstraction_Factory_MDB2.php');
                $factory = new DB_Abstraction_Factory_MDB2();
            break;
        }
        return $factory;
    }
 
}

The Abstract Factory now consolidates all that decision making for which library to use and leaves our code free to ignore specific implementations, i.e. it needn't know the name of the Factory to use since all Factories use the same predictable interface. Code to an interface, not an implementation.

Our client code is now simplified to a large extent. We have consolidated creational logic to Factories, and even consolidated the decision on which library/Factory to use to an Abstract Factory. The only code we require to utilize this new structure is almost ridiculously simple.

php
// instantiate Abstract Factory
$abstractfactory = new DB_Abstraction_AbstractFactory();
 
// fetch a concrete Factory (decision handled in Abstract Factory static method)
$factory = $abstractfactory::getFactory();
 
// use concrete Factory to create a database connection object from
// the selected database abstraction library
$db = $factory->createInstance();

Disadvantages

To be added

Conclusion

Situations may arise where multiple Factories exist to create objects with similar responsibilities but with varying creational logic. In such situations, further simplification of object creation might be introduced using the Abstract Factory Pattern. The Abstract Factory pattern free client code from worrying about specific implementations and lets Abstract Factories and concrete Factories do all the hard work.

This consolidation of logic can be expanded into more complex scenarios where multiple Factories exist, and we need to simplify the logic which determines which Factory should be used. In some cases Factories may group families of many related classes in which case the Abstract Factory pattern graduates from a simplification to a near requirement.

A more complex second example is to be added in the future.

Retrieved from "/wiki/Abstract_Factory"
MediaWiki
GNU Free Documentation License 1.2