Press "Enter" to skip to content

Factory Method (PHP)

Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Benefit:

  • You avoid tight coupling between the creator and the concrete products. Single Responsibility Principle.
  • You can move the product creation code into one place in the program, making the code easier to support. Open/Closed Principle.
  • You can introduce new types of products into the program without breaking existing client code.

Disadvantage:

  • The code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern. The best case scenario is when you’re introducing the pattern into an existing hierarchy of creator classes.

Why?

Adding a new class to the program isn’t that simple if the rest of the code is already coupled to existing classes. For example, the original version of your app can only handle transportation by trucks, so the bulk of your code lives inside the Truck class.

After a while, your app becomes very popular. Suddenly, you receive dozens of requests from sea transportation companies to ask you to implement sea logistics into the app. (Ship class)

Sample code:

<?php

namespace RefactoringGuru\FactoryMethod\Conceptual;

abstract class Creator
{
    abstract public function factoryMethod(): Product;

    /**
     * Despite its name, the Creator's primary responsibility is
     * not creating products. Usually, it contains some core 
     * business logic that relies on Product objects,
     * returned by the factory method. Subclasses can
     * indirectly change that business logic by overriding
     * the factory method
     * and returning a different type of product from it.
     */
    public function someOperation(): string
    {
        // Call the factory method to create a Product object.
        $product = $this->factoryMethod();
        // Now, use the product.
        $result = "Creator: The same creator's code has just worked with " . $product->operation();

        return $result;
    }
}


class ConcreteCreator1 extends Creator
{
    public function factoryMethod(): Product
    {
        return new ConcreteProduct1();
    }
}

class ConcreteCreator2 extends Creator
{
    public function factoryMethod(): Product
    {
        return new ConcreteProduct2();
    }
}

/**
 * The Product interface declares the operations that all 
 * concrete products must implement.
 */
interface Product
{
    public function operation(): string;
}

/**
 * Concrete Products provide various implementations of the 
 * Product interface.
 */
class ConcreteProduct1 implements Product
{
    public function operation(): string
    {
        return "{Result of the ConcreteProduct1}";
    }
}

class ConcreteProduct2 implements Product
{
    public function operation(): string
    {
        return "{Result of the ConcreteProduct2}";
    }
}

/**
 * The client code works with an instance of a concrete 
 * creator, albeit through
 * its base interface. As long as the client keeps working 
 * with the creator via
 * the base interface, you can pass it any creator's subclass.
 */
function clientCode(Creator $creator)
{
    // ...
    echo "Client: I'm not aware of the creator's class, but it still works.\n" . $creator->someOperation();
    // ...
}

echo "App: Launched with the ConcreteCreator1.\n";
clientCode(new ConcreteCreator1());
echo "\n\n";

echo "App: Launched with the ConcreteCreator2.\n";
clientCode(new ConcreteCreator2());

 Output:

App: Launched with the ConcreteCreator1.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct1}

App: Launched with the ConcreteCreator2.
Client: I'm not aware of the creator's class, but it still works.
Creator: The same creator's code has just worked with {Result of the ConcreteProduct2}