Understanding the Factory Method Design Pattern (Перевести)

Building things can be tough when you can’t anticipate what type of objects you’ll need to create or how to create them. Take for example a factory which manufactures a large number of products. Each product can be made up of any number of components in their inventory. The workers know what’s in the inventory but don’t necessarily know beforehand what kind of products they will be making. The Factory Method design pattern can be applied to similar situations in programming where you have a set of component classes but won’t know exactly which one you’ll need to instantiate until runtime. In this article I’ll show you how the Factory Method pattern can be used to create different objects, without knowing beforehand what sort of objects it needs to create or how the object is created.

The Factory Method

The Factory Method pattern is a design pattern used to define a runtime interface for creating an object. It’s called a factory because it creates various types of objects without necessarily knowing what kind of object it creates or how to create it.

Here’s an example of how the Factory Pattern works. Assume you have a ProductFactory class which creates a new type of product:

<?php
class ProductFactory
{
    public static function build($type) {
        // assumes the use of an autoloader
        $product = "Product_" . $type;
        if (class_exists($product)) {
            return new $product();
        }
        else {
            throw new Exception("Invalid product type given.");
        }
    } 
}

By defining build() as a factory method, you now have an interface through which you can create different products on the fly.

<?php
// build a new Product_Computer type
$myComputer = ProductFactory::build("Computer");
// build a new Product_Tablet type
$myTablet = ProductFactory::build("Tablet");

The Factory Method pattern is generally used in the following situations:

  • A class cannot anticipate the type of objects it needs to create beforehand.
  • A class requires its subclasses to specify the objects it creates.
  • You want to localize the logic to instantiate a complex object.

The Factory Method pattern is useful when you need to abstract the creation of an object away from its actual implementation. Let’s say the factory will be building a “MobileDevice” product type. A mobile device could be made up of any number of components, some of which can and will change later, depending on advances in technology.

<?php
class Product_MobileDevice
{
    private $components;

    public function __construct() {
        // this device uses a 7" LCD
        $this->addComponent(ProductFactory::build("LCD", 7));
        // and features an 1GHz ARM processor  
        $this->addComponent(ProductFactory::build("CPU_ARM", 1));
    }
...
}

// build a new Product_MobileDevice type
$myDevice = ProductFactory::build("MobileDevice");
$myDevice->use();

The logic to create a Product_MobileDevice object has been encapsulated into the class itself. If you want to exchange the 7″ LCD screen with a 10″ Touch_Screen later, you can make the isolated change in the MobileDevice class without affecting the rest of your application.

Factories Using Other Factories

Because the instantiation of an object is encapsulated, it could also use factories itself. To further expand on the idea of abstract object creation, let’s use a non-software engineering analogy. An automotive factory manufactures vehicles of a specific make, model, and color, but it may not produce all the necessary parts itself that are required to build the vehicle. In other words, it delegates the production of these parts out to other factories which it then uses to build new vehicles.

Under this scenario, a vehicle factory might look like this:

<?php
class VehicleFactory
{
    public static function build($make, $model, $color) {
        $vehicle = new Vehicle;

        // vehicle needs a chassis which is produced by another factory
        $vehicle->addPart(VehicleChassisFactory::build($make, $model));
        // needs an engine built by someone else
        $vehicle->addPart(VehicleEngineFactory::build($make, $model));
        // it needs a bodykit made by another factory
        $vehicle->addPart(VehicleBodyFactory::build($make, $model, $color));
        // and interiors are made by... you guessed it, someone else 
        $vehicle->addPart(VehicleInteriorFactory::build($make, $model, $color));

        // ...add more parts

        return $vehicle;
    }
}

// build a new white VW Amarok
$myCar = VehicleFactory::build("Volkswagon", "Amarok", "white");
$myCar->drive();

Voilà! A shiny new car. The VehicleFactory class produces a vehicle of a specified make, model and color, but acquired the various parts produced by other factory methods.

Summary

In this article you’ve learned how the Factory Method pattern can be used to localize the construction of different objects and to allow you to create objects without knowing specifically what type you’ll need beforehand. You also saw also factory methods can use other factory methods to create objects objects and define what objects they produce.