Design Patterns in PHP: The Factory Pattern

I'm going to be writing a series of articles that cover different design patterns in PHP. To kick off this series, we're going to start with the Factory Pattern.

To begin the series though, I want to discuss what a design pattern is and why they're helpful. Design patterns happen 'organically' when we're building OOP applications, regardless of language. What I mean by that is, even if we're unaware of specific design patterns we are probably already using them, and have just discovered them on our own, with out knowing they already had a name.

The concept of a design pattern was originally created in the Architecture world (actual buildings). Architects had a set of patterns that they would utilize to solve engineering challenges with certain structure designs. A good visual example of this might be different types of bridges. We have arch, beam, cable-stayed, cantilever, suspension, and truss bridges.


Image source

Each one of these bridge types is a Design Pattern to solve the problem, of how do I build a bridge.

Likewise in programming we have design patterns to help solve different types of problems.

The Factory Design Pattern solves the problem of having to dynamically instantiate a class based on other variables.

An example of that might be we have a program that allows an Architect to design a bridge. Before we get to the screen that may ask specific details, we need to know what kind of bridge we're building. Programmatically this translates into we have a BridgeDesigner Class that requires a BridgeType Class that implements a BridgeType Interface, and we have 6 different bridge type classes. We have an ArchType, BeamType, etc class for each type of bridge.

Now the BridgeDesigner Class needs to know which type class we're actually using.

This is where a Factory Class can come in handy.

Let's look at some actual code.

Here is our factory:

Class TypeFactory
{
    const NS = "App\\BridgeTypes\\";
    public function __construct()
    {
        //
    }

    public static function buildType($type = null)
    {
        if(!is_null($type)){
            $class = self::NS.$type;
            if(class_exists($class)){
                if($class instanceof BridgeTypeInterface){
                    return new $class();
                }
                throw new NeedsToImplementInterfaceException;
            }
            throw new TypeNotFoundException;
        }
    }
}

Here is an example of a Type Class:

Class ArchType implements BridgeTypeInterface
{
    public function __construct()
    {
        //
    }
}

Here is an example of our BridgeDesigner Class:

Class  BridgeDesigner 
{
    private $bridgeType;
    public function __construct($type)
    {
        $this->bridgeType = TypeFactory::buildType($type);
    }

}

The Factory Class is pretty simple, we really have a single method buildType that accepts a string of a class name.

The method first checks if the $type value is set. If so it builds the complete class path using our Name Space Constant (NS). It then does a few checks. First we make sure this class exists, if it does, we verify it's implementing our BridgeTypeInterface.

If all of the checks pass, we instantiate the BridgeType class, and return it to the BridgeDesigner class.

This makes things like having a user select a type, that basically passes a string in POST data, we can use that string to build a class and use it in our BridgeDesigner Class.

An alternative option would be to use the factory to build the class, then pass the class to our BridgeDesigner class. Which would allow us to do something slightly different.

Class  BridgeDesigner 
{
    private $bridgeType;
    public function __construct(BridgeTypeInterface $type)
    {
        $this->bridgeType = $type;
    }

}

Here we are using dependency inversion and making sure we only accept a Class that implements the BridgeTypeInterface.

As you can see the Factory pattern provides us with some interesting flexibility by allowing us to dynamically instantiate a class.

For another example of how I have used the Factory Design pattern take a look at this article.