Patterns For PHP
Index Patterns News Forums

Factory

From Patterns For PHP

Contents

Introduction

At some point in PHP development, programmers will discover a need to simplify the creation of some objects. Perhaps the object simply requires a number of detailed steps to create, all of which must be duplicated for every new object of that type. To remove such code duplication and simplify the task, the Factory Pattern offers a solution. In fact the Factory Pattern is something many programmers may discover by themselves since it is a logical step to take all the detailed creational steps of an object and try bundling into a new object to handle the task.

The Factory Pattern can be applied as an individual class with sole responsibility for creating objects or as a single method within the object to be created (a Factory Method). In both cases, its purpose remains similar - to make creating an object easier.

Definition

Definition: An object implementing the Factory Pattern is an object which creates other objects of a specific type.

A PHP 5 Example

One family of objects whose creation may require numerous complex steps are from Database Abstraction libraries such as the ADOdb Lite or PEAR::MDB2. These libraries involve instantiating a connection object, allowing plugin use, and connecting to a database using configuration options. The steps involved can range from simple two liners to using complex logic depending on the scenario and plugins required.

Simplifying this process makes huge sense. One can write a dedicated Factory class to fetch configuration options and perform the instantiation and plugin tasks transparently (based on those options). A simple Factory interface and implementing class (with minimal tasks) for ADOdb Lite might look similar to:

php
interface iFactory {
 
    public function createInstance();
 
}
 
class DatabaseAbstractionFactory implements iFactory {
 
    private $settings = array();
 
    public function __construct() {
        // Settings class from our Singleton Pattern example
        $this->settings = Settings::getInstance();
    }
 
    public function createInstance() {
        require_once('/path/to/adodb_lite/adodb.inc.php');
        $dsn = $this->settings['db_dsnstring'];
        // do lots of complicated stuff for adding plugins
        $db = ADONewConnection($dsn);
        return $db;
    }
 
}

Implementing the Factory Pattern

Of course, there are many other similar uses for a Factory. All that is required is that the Factory consolidate all the logic required to create an object. In the case above we have a single complex creation process for a Database Abstraction library's connection object. Another situation where a Factory is of use is where different objects of the same family must be created depending on some value.

For example, we may have a Logging utility which is capable of outputting a log message either to the browser or to the command line. Or perhaps we want it stored for later viewing to a file. The typical logic used within our calling class to determine which to use may be similar to:

php
switch($outsetting)
{
    case 'echo':
        $out = new Output_Echo();
        break;
    case 'cli':
        $out = new Output_CLI();
        break;
    case 'file':
        $out = new Output_File();
}

This would work at first. But what happens if we decide we need to store the log message for later viewing in a database? Or maybe a client wants it output as formatted XML? As the options grow, this switch statement is growing ungainly. Worse, we may be replicating this piece of code across numerous classes which handle the initial generation or formatting of the log message. Any changes would need to be manually updated to all those classes.

The Factory Pattern suggests this creational logic should be isolated within its own class (the Factory) leaving the calling objects free to do their stuff and let the Factory worry about what Output object might be required.

The result may look similar to:

php
interface iFactory {
 
    public function createInstance();
 
}
 
class Output_Factory implements iFactory {
 
    private $settings = array();
 
    public function __construct() {
        $this->settings = Settings::getInstance();
    }
 
    public function createInstance() {
        $outsetting = $this->settings->get('out.target');
        switch($outsetting)
        {
            case 'echo':
                $out = new Output_Echo();
                break;
            case 'cli':
                $out = new Output_CLI();
                break;
            case 'file':
                $out = new Output_File();
        }
        return $out;
    }
 
}

With all the troublesome logic hidden from the calling object and a standard interface implemented that is common to all Output classes, out calling classes (however many we have) are free to ignore everything except getting an Output class via the Factory and telling it to write something...

php
class My_Log {
 
    private $output = null;
 
    public function __construct() {
        $factory = new Output_Factory();
        $this->output = $factory->createInstance();
    }
 
    public function log($message) {
        $this->output->write($message);
    }
 
}

If creating the Factory manually turns annoying (being one extra line, and all programmers being exceptionally lazy), you could bundle the Settings class retrieval into the createInstance() method on the Factory class and make it a static method.

Disadvantages

The Factory Pattern is a common pattern to see implemented. It is a logical method of isolating the logic for creating a class, or the logic for deciding which concrete subclass of a family to instantiate. In this capacity it has few disadvantages. Some problems may arise when complex creational logic of various types co-exist - i.e. where it becomes necessary to utilise two or more Factory classes.

One example might be our Database Abstraction Factory. It's sole purpose is to churn out ADOdb Lite connection objects applying configuration options, loading required plugins (if such added), etc. If someone were to decide that they needed access to PEAR::MDB2 or the traditional ADOdb library then the creational logic for instantiation and plugin loading would differ. Although this new piece of logic (deciding which library to use) could be integrated into the existing Factory class - it would simply turn into a very complex switch statement.

A solution to this scenario, the Abstract Factory Pattern, is discussed in another entry. The Abstract Factory abstracts the logic of choosing which Factory to use in creating a new object. Sound complicated? It's not :).

Conclusion

In covering the Factory Pattern we have covered a lot of ground. Creating objects can range from simple to complex, and the Factory Pattern provides a solution for isolating such creational logic in centralised class to allow easier object creation by client classes and prevent code duplication. Although we saw two varied situations where the Factory Pattern was useful, the Pattern can prove useful in many situations that call for the creation of an object.

Also noted is that 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.

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