The Factory Method Patterm (The Factory Pattern – part 2)

1. Definition

Defines an interface for creating an object, but lets subclasses decide which class to instantiate.

Factory Method lets a class defer instantiation to subclasses.

2.Case study

I’m from Japan, where you’ll see different types of Miso (an ingredient of traditional soup) at the supermarket in different regions.

For example, in Eastern areas, dark red Miso is popular, whereas in Western areas you are more likely to find light yellow Miso at the supermarket.

In this case study, your job as a web developer is to create an inventory management system in PHP for a big supermarket chain in Japan.

(Of course, you can replace Miso/Japan with anything familiar to you.)

2.1 The problem

One thing the inventory management system has to do is to get products from a supplier.

To develop this system, you have to make sure that different stores can get different products of the same type from different suppliers, because different stores sell different products based on their location.

For example, stores located in eastern areas have to sell dark red miso, and ones located in western areas sell light yellow miso.

$easternStore = new Store();
$easternStore->replenishMiso();
//Getting packages of Dark Red Miso

$westernStore = new Store();
$westernStore->replenishMiso();
//Getting packages of Light Yellow Miso

Here is a class we can potentially create for each store.

class Store
{
    private $supplier;
    private $stock;

    public function __construct(Supplier $supplier)
    {
        $this->supplier = $supplier;
    }    

    public function replenishMiso($type, $amount)
    {
        $stock['miso'] = $this->supplier->supplyMiso($type, $amount);
    }

    //Of course you'll need other methods here.
    //Super markets stock many other things.
}

This class is using the Simple Factory pattern, even though we call the factory “Supplier” here.

How do the suppliers (simple factories) look like?

class EasternSupplier
{
    public function supplyMiso($type, $amount)
    {
        $products = [];
        for ($i = 0; $i < $amount; $i++) {
            if ($type === "rice") {
                $products[] = new RedRiceMiso();
            } else if ($type === "soy") {
                $products[] = new RedSoyMiso();
            } else if ($type === "barley") {
                $products[] = new RedBarleyMiso();
            }
        }
        return $products;
    }
}
//WesternSupplier returns different products

Let’s see how those imaginary classes would work.

$easternStore = new Store(new EasternSupplier);
$easternStore->replenishMiso("rice", 30);
//Getting 30 packages of RedRiceMiso

$westernStore = new Store(new WesternSupplier);
$westernStore->replenishMiso("soy", 40);
//Getting 40 packages of YellowSoyMiso

Now it seems like each store is able to get different products based on the location… but here is another thing we have to consider.

The issue is that different stores need different ways of managing their products.

In some stores, for instance, each type of products has to be stored in different storages. (i.e. we need to use associated arrays to store the objects)

class Store
{
    public function replenishMiso($type, $amount)
    {
        $stock['miso'][$type] = $this->supplier->supplyMiso($type, $amount);
        //$stock is no longer a simple array.
    }
}

In other stores, once they get new products from their suppliers, they need to call another function before storing those products. (i.e. we need to put an if statement)

class Store
{
    public function replenishMiso($type, $amount)
    {
        if ($type === "rice") {
            $this->updateLog($type, $amount);
            //Log when the products were ordered.
        }
        $stock['miso'] = $this->supplier->supplyMiso($type, $amount);
    }    
}

In other words, once the objects are created by the factory (products are delivered to stores), each store has to manage those objects in their own way.

How can we allow the stores to get the appropriate products, while allowing them to manage their products?

One way to achieve this is to use an abstract class (or an interface) with the Factory method.

2.2 The solution

Let’s consider a framework for the stores so that…

(A) the stores can localize the object creation logic, and

(B) the stores can manage the objects in the way they want.

1. put back the supplyMiso() function to the store class, but this time supplyMiso() function and Store class are abstract.

abstract class Store
{
    private $stock;

    public function replenishMiso($type, $amount)
    {
        $stock = $this->supplyMiso($type, $amount);
    }

    abstract public function supplyMiso($type, $amount);
}

2. allow the subclasses to decide how the objects are created

class EasternStore extends Store
{
    public function supplyMiso($type, $amount)
    {
        $products = [];
        for ($i = 0; $i < $amount; $i++) {
            if ($type === "rice") {
                $products[] = new RedRiceMiso();
            } else if ($type === "soy") {
                $products[] = new RedSoyMiso();
            } else if ($type === "barley") {
                $products[] = new RedBarleyMiso();
            }
        }
        return $products;
    }
}

3. override the function for managing the objects accordingly.

class EasternStore extends Store
{
    public function replenishMiso($type, $amount)
    {
        $stock[$type] = $this->supplyMiso($type, $amount);
        //Updated $stock into an associative array
    }
}

2.3 The outcome

Here is what we’ve got.

$easternStore = new EasternStore();
$easternStore->replenishMiso("rice", 30);

In this way, each subclass can determine which class to instantiate and manage objects (even though how those products are managed isn’t visible).

3. The structure

On a side note, in most textbooks about design patterns, you’ll see that the concrete products (in our case, RedRiceMiso, RedSoyMiso etc) implement the same interface so that all the creators are concerned about one type of product.

I omitted to create the interface for concrete products because I want you to focus on how the Factory method allows subclasses to decide which class to instantiate in this article.

4. When to use this pattern

When you need different subclasses who create different instances of the same type and the subclasses share a function which operates on the instances, this pattern becomes handy.

In other words, it is when you need a framework which allows each subclass to instantiate their own domain instances.

If you just need to be able to instantiate different classes and don’t need to subclass the creator, you can use the Simple Factory, which won’t have anything other than the object creation method(s).

Leave a Reply

Your email address will not be published. Required fields are marked *