Back
Design Patterns
The Abstract Factory Pattern
Implementing Abstract Factory
Forward


The Abstract Factory Pattern

If you have used the PEAR DB package, you've used this pattern; there is an explicit DB::factory() method, as well as what you've probably used: DB::connect(), which calls factory() and then connects to the database as well.

The purpose of the abstract factory pattern is to provide an interface for creating one of a family of objects without specifying that object's concrete class name. This lets you add new subclasses to a driver architecture and use them solely by changing a configuration file, instead of having to dig out hardcoded class names.

In the DB:: class, you call DB::connect() with the type of database you want to connect to, and any options for that database. The abstract factory method takes care of loading the appropriate subclass, instantiates it, and then passes the arguments along. Here's what it looks like:

<?php

require_once 'DB.php';

$type 'mysql';
$options = array('username' => 'foo',
                 
'...'      => '...');

$dbh = &DB::connect($type$options);

... and voila, the $dbh variable is a MySQL database driver. If you wanted Oracle instead, just change $type - which might come from a configuration file, a database field, anywhere - to 'oci8', the name of the oracle driver. Without the abstract factory, this is what you'd have to do:

<?php

require_once 'DB.php';
require_once 
'DB/mysql.php';

$options = array('username' => 'foo',
                 
'...'      => '...');

$dbh = &new DB_mysql($options);
$dbh->connect();

... and if you wanted to change to Oracle, you'd have to change all of those filename references (DB/mysql.php) and class references (DB_mysql) to the Oracle versions.

If you're wondering why we're not mentioning the factory pattern, I haven't seen it used very often in PHP. The factory pattern differs from abstract factory in that you are deferring instantiation to a subclass. An example would be if you had a Logging package, and different Analyzer classes for each different Logger subclass. Logger::getAnalyzer() would be a factory method that returned the correct Analyzer object; the Logger subclass would know which class to instantiate.