Back
The Abstract Factory Pattern
Implementing Abstract Factory
Singleton
Forward


Implementing Abstract Factory

In essence, this is a pretty simple pattern. You can make it more complicated depending on how flexible or inflexible your scheme to load possible subclasses is, but the implementation from DB:: is about as simple as it gets:

<?php
    
/**
     * Create a new DB connection object for the specified database
     * type
     *
     * @param string $type database type, for example "mysql"
     *
     * @return mixed a newly created DB object, or a DB error code on
     * error
     *
     * access public
     */
    
function &factory($type)
    {
        @include_once(
"DB/${type}.php");

        
$classname "DB_${type}";

        if (!
class_exists($classname)) {
            return 
PEAR::raiseError(nullDB_ERROR_NOT_FOUND,
                                    
nullnullnull'DB_Error'true);
        }

        @
$obj =& new $classname;

        return 
$obj;
    }

The function tries to load DB/$type.php from somewhere in the include_path; if after doing that the class isn't defined, it throws an error; otherwise it returns an instance of that class.

Here's a slightly more complicated example, from Horde's Auth library. This implementation does a bit more checking on the requested driver, including case checking, searching elsewhere in the include_path for the driver, and allowing an individual Horde application to implement a driver.

<?php
    
/**
     * Attempts to return a concrete Auth instance based on $driver.
     *
     * @access public
     *
     * @param mixed $driver           The type of concrete Auth subclass to
     *                                return. This is based on the storage
     *                                driver ($driver). The code is dynamically
     *                                included. If $driver is an array, then we
     *                                will look in $driver[0]/lib/Auth/ for
     *                                the subclass implementation named
     *                                $driver[1].php.
     * @param optional array $params  A hash containing any additional
     *                                configuration or connection parameters a
     *                                subclass might need.
     *
     * @return object Auth   The newly created concrete Auth instance, or false
     *                       on an error.
     */
    
function &factory($driver$params null)
    {
        if (
is_array($driver)) {
            list(
$app$driver) = $driver;
        }

        
$driver strtolower(basename($driver));

        if (empty(
$driver) || (strcmp($driver'none') == 0)) {
            return new 
Auth;
        }

        if (
is_null($params)) {
            
$params Horde::getDriverConfig('auth'$driver);
        }

        if (!empty(
$app)) {
            include_once 
$GLOBALS['registry']->getParam('fileroot'$app) . '/lib/Auth/' $driver '.php';
        } elseif (@
file_exists(dirname(__FILE__) . '/Auth/' $driver '.php')) {
            include_once 
dirname(__FILE__) . '/Auth/' $driver '.php';
        } else {
            @include_once 
'Horde/Auth/' $driver '.php';
        }
        
$class 'Auth_' $driver;
        if (
class_exists($class)) {
            return new 
$class($params);
        } else {
            return 
PEAR::raiseError('Class definition of ' $class ' not found.');
        }
    }

You can imagine variations from there - allowing multiple spellings of a driver, having a lookup map for a driver, silently mapping certain things to the same implementation, passing extra information parameters to the created object, etc.