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(null, DB_ERROR_NOT_FOUND, null, null, null, '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.
|