Design Pattern: Abstract Factory Pattern in PHP

Abstract factory pattern is used when we need multiple factories which produce related but different sets of objects. This pattern is generally used in relatively larger applications that deal with lots of classes of related classes but can be divided into multiple groups based on some criteria.

As Abstract factory pattern is a factory of the Factory pattern, so before learning the details of the Abstract factory pattern make sure to have a clear idea about Factory pattern.

This article discusses the Abstract Factory pattern implementations in PHP. Check the implementation details and examples.

Implementation

Use the following steps for the implementation-

  • Create interface for the item/product classes.
  • Create item/product classes and implement the item interface.
  • Create an abstract factory. This can be an interface or abstract class. This has the declaration of method which are responsible for generating item/product objects.
  • Create factory classes and implement the abstract factory interface/class. These classes contain method for generating item/product objects.
  • Create a factory producer class. This class is responsible for generating a factory object and returning that.

Here is a simple Abstract factory implementation in PHP. This is not any specific example, just demo/sample code.

<?php
// Abstract factory implementation in PHP

// Item interface
interface ItemInterface {
    function operation1(): void;
    function operation2(): void;
}

// Item of type 1
class Type1Item1 implements ItemInterface {
    public function operation1(): void {
        echo "Type 1 Item 1: executing operation 1\n";
    }

    public function operation2(): void {
        echo "Type 1 Item 1: executing operation 2\n";
    }
}

// Item of type 1
class Type1Item2 implements ItemInterface {
    public function operation1(): void {
        echo "Type 1 Item 2: executing operation 1\n";
    }

    public function operation2(): void {
        echo "Type 1 Item 2: executing operation 2\n";
    }
}

// Item of type 2
class Type2Item1 implements ItemInterface {
    public function operation1(): void {
        echo "Type 2 Item 1: executing operation 1\n";
    }

    public function operation2(): void {
        echo "Type 2 Item 1: executing operation 2\n";
    }
}

// Item of type 2
class Type2Item2 implements ItemInterface {
    public function operation1(): void {
        echo "Type 2 Item 2: executing operation 1\n";
    }

    public function operation2(): void {
        echo "Type 2 Item 2: executing operation 2\n";
    }
}

// Factory interface
interface FactoryInterface {
    function getItem(string $type): ?ItemInterface;
}

// Type 1 factory
class Type1Factory implements FactoryInterface {
    public function getItem(string $itemIdentifier): ?ItemInterface {
        switch (strtolower($itemIdentifier)) {
            case "item1": return new Type1Item1();
            case "item2": return new Type1Item2();
        }

        return null;
    }
}

// Type 2 factory
class Type2Factory implements FactoryInterface {
    public function getItem(string $itemIdentifier): ?ItemInterface {
        switch (strtolower($itemIdentifier)) {
            case "item1": return new Type2Item1();
            case "item2": return new Type2Item2();
        }

        return null;
    }
}

// Factory producer - Factory of Factory
class FactoryProducer {
    public static function getFactory(int $type): ?FactoryInterface {
        switch ($type) {
            case 1: return new Type1Factory();
            case 2: return new Type2Factory();
        }

        return null;
    }
}

// Demo
$factory1 = FactoryProducer::getFactory(1);

$item1 = $factory1->getItem("item1");
$item1->operation1();
$item1->operation2();

$factory2 = FactoryProducer::getFactory(2);

$item2 = $factory2->getItem("item1");
$item2->operation1();

$item3 = $factory2->getItem("item2");
$item3->operation1();
$item3->operation2();

Above code will generate the following output:

Type 1 Item 1: executing operation 1
Type 1 Item 1: executing operation 2

Type 2 Item 1: executing operation 1

Type 2 Item 2: executing operation 1
Type 2 Item 2: executing operation 2

Examples

Let’s look at a few examples of Abstract factory pattern implementation in PHP.

Example #1: Transport

Here we have 2 types of transport – two and four wheelers. We want some factory mechanism for generating transport objects.

As the object generation process of different types of transports can be different, so we want to use separate factories for different types of objects.

Transport Interface [Item Interface]

  • Create file “Transport.php”.
  • Define interface “Transport” and declare required methods.
<?php
// Transport.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

interface Transport {
    public function start(): void;
    public function stop(): void;
    public function repair(): void;
}

Bicycle Class [Item Class]

  • Create file “Bicycle.php”.
  • Create class “Bicycle”.
  • Implement the “Transport” interface for the class. Define methods that are declared in the interface.
<?php
// Bicycle.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;


class Bicycle implements Transport {
    public function start(): void {
        echo "Bicycle Started\n";
    }

    public function stop(): void {
        echo "Bicycle Stopped\n";
    }

    public function repair(): void {
        echo "Bicycle Repair\n";
    }
}

Motorcycle Class [Item Class]

  • Create file “Motorcycle.php”.
  • Create “Motorcycle” class and implement “Transport” interface.
<?php
// Motorcycle.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

class Motorcycle implements Transport {
    public function start(): void {
        echo "Motorcycle Started\n";
    }

    public function stop(): void {
        echo "Motorcycle Stopped\n";
    }

    public function repair(): void {
        echo "Motorcycle Repair\n";
    }
}

Car Class [Item Class]

  • Create a file “Car.php”.
  • Define “Car” class and implement the “Transport” interface.
<?php
// Car.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

class Car implements Transport {
    public function start(): void {
        echo "Car Started\n";
    }

    public function stop(): void {
        echo "Car Stopped\n";
    }

    public function repair(): void {
        echo "Car Repair\n";
    }
}

Truck Class [Item Class]

  • Create file “Truck.php”.
  • Define “Truck” class and implement the “Transport” interface.
<?php
// Truck.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

class Truck implements Transport {
    public function start(): void {
        echo "Truck Started\n";
    }

    public function stop(): void {
        echo "Truck Stopped\n";
    }

    public function repair(): void {
        echo "Truck Repair\n";
    }
}

Abstract Transport Factory Interface [Factory Interface]

  • Create a file named “AbstractTransportFactory.php”.
  • Define interface “AbstractTransportFactory”.
  • Declare method “getTransport”, this takes a “$type” param and generates a factory based on that.

This can be an abstract class, if required.

The “getTransport” method can be static, if we prefer.

<?php
// AbstractTransportFactory.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;


interface AbstractTransportFactory {
    public function getTransport(string $type): ?Transport;
}

Two-Wheel Transport Factory Class [Factory Class]

  • Create file “TwoWheelTransportFactory.php”.
  • Define the class “TwoWheelTransportFactory” and implement interface “AbstractTransportFactory” for the class.
  • In the “getTransport” method check the “$type” parameter and generate a two-wheel class(Bicycle, Motorcycle) object based on that.
<?php
// TwoWheelTransportFactory.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

class TwoWheelTransportFactory implements AbstractTransportFactory {
    public function getTransport(string $type): ?Transport {
        if (strcasecmp($type, "bicycle") === 0) {
            return new Bicycle();
        }

        if (strcasecmp($type, "motorcycle") === 0) {
            return new Motorcycle();
        }

        return null;
    }
}

Four-Wheel Transport Factory Class [Factory Class]

  • Create file “FourWheelTransportFactory.php”.
  • Define the class “FourWheelTransportFactory” and implement the interface “AbstractTransportFactory” for the class.
  • In the “getTransport” method check the “$type” parameter and generate a two-wheel class(Car, Truck) object based on that.
  • Create a file named “four-wheel-transport-factory.ts”.
  • Define factory class “FourWheelTransportFactory”, and implement “AbstractTransportFactory” for the class.
<?php
// FourWheelTransportFactory.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

class FourWheelTransportFactory implements AbstractTransportFactory {
    public function getTransport(string $type): ?Transport {
        if (strcasecmp($type, "car") === 0) {
            return new Car();
        }

        if (strcasecmp($type, "truck") === 0) {
            return new Truck();
        }

        return null;
    }
}

Factory Producer Class [Producer]

  • Create file “FactoryProducer.php”.
  • Define class “FactoryProducer”.
  • Create method “getFactory”. This method accepts param “$numberOfWheels”, and based on the number of wheels it generates an object of “TwoWheelTransportFactory” or “FourWheelTransportFactory”.

The Factory objects generated by this Factory producer will be used in the next steps to produce item/product objects.

<?php
// FactoryProducer.php

namespace BigBoxCode\DesignPattern\AbstractFactory\Transport;

class FactoryProducer {
    public static function getFactory(int $numberOfWheels): ?AbstractTransportFactory {
        switch ($numberOfWheels) {
            case 2:
                return new TwoWheelTransportFactory();
            case 4:
                return new FourWheelTransportFactory();
        }

        return null;
    }
}

Demo

Use the “getFactory” method to generate a factory. And use that factory to generate transport objecs.

<?php
// demo.php

require __DIR__ . '/../../vendor/autoload.php';

use BigBoxCode\DesignPattern\AbstractFactory\Transport\FactoryProducer;

$transportFactory1 = FactoryProducer::getFactory(2);
$transport1 = $transportFactory1?->getTransport("bicycle");

$transport1?->start();

$transportFactory2 = FactoryProducer::getFactory(4);
$transport2 = $transportFactory2?->getTransport("truck");

$transport2?->start();

Output

Output of the code will be as below-

Bicycle Started
Truck Started

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Abstract Factory pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.