PHP Abstract Factory Design Pattern Creational Pattern

PHP Abstract Factory Pattern

A short screencast: a family of products and factories is built on shared interfaces — the Abstract Factory pattern in PHP.

Abstract Factory Pattern — open the lesson page

Overview

In this screencast we build the pattern on interfaces. ProductInterface describes a product, FactoryInterface describes a factory. Concrete factories LaptopFactory and SmartphoneFactory create their products, while the client code (demo.php) works only with abstractions — you can swap one "family" of objects for another without changing the calling code.

UML diagram

classDiagram class ProductInterface { <<interface>> +getInfo() string } class FactoryInterface { <<interface>> +createProduct() ProductInterface } class Laptop { -string name +__construct(name: string) +getInfo() string } class Smartphone { -string model +__construct(model: string) +getInfo() string } class LaptopFactory { +createProduct() ProductInterface } class SmartphoneFactory { +createProduct() ProductInterface } Laptop ..|> ProductInterface Smartphone ..|> ProductInterface LaptopFactory ..|> FactoryInterface SmartphoneFactory ..|> FactoryInterface LaptopFactory ..> Laptop : creates SmartphoneFactory ..> Smartphone : creates FactoryInterface --> ProductInterface : returns

Code

ProductInterface.php

interface ProductInterface
{
    public function getInfo(): string;
}

Laptop.php

class Laptop implements ProductInterface
{
    public function __construct(
        private string $name
    ) {}

    public function getInfo(): string
    {
        return 'Laptop: ' . $this->name;
    }
}

Smartphone.php

class Smartphone implements ProductInterface
{
    public function __construct(
        private string $model
    ) {}

    public function getInfo(): string
    {
        return 'Smartphone: ' . $this->model;
    }
}

FactoryInterface.php

interface FactoryInterface
{
    public function createProduct(): ProductInterface;
}

LaptopFactory.php

class LaptopFactory implements FactoryInterface
{
    public function createProduct(): ProductInterface
    {
        return new Laptop('MacBook Pro');
    }
}

demo.php

require_once 'ProductInterface.php';
require_once 'Laptop.php';
require_once 'FactoryInterface.php';
require_once 'LaptopFactory.php';

$factory = new LaptopFactory();
$product = $factory->createProduct();

echo $product->getInfo();

Note: why interfaces

The client in demo.php knows nothing about Laptop and LaptopFactory — only ProductInterface and FactoryInterface. That is the essence of the pattern: the code depends on abstractions, and the concrete factory decides which "family" of objects to assemble. Add a SmartphoneFactory, and the client assembles a smartphone without any changes to the calling code.

Note: interface, not an abstract class

In PHP a class can extend only one abstract class but can implement any number of interfaces. Members of one "family" in the Abstract Factory usually share no implementation — they are bound only by a contract, so an interface is the right abstraction:

abstract class AbstractProduct
{
    abstract public function getInfo(): string;
}

Such an abstraction immediately consumes the only inheritance slot: Laptop could no longer extend, say, the framework's base model class. Besides, an abstract class mixes contract and implementation — putting a shared method there drags it into every product of the family whether it needs it or not, making the family harder to swap. An interface defines only the type and behavior, leaving the implementation to each product.

Example output

Running php demo.php in the terminal produces:

Laptop: MacBook Pro

Advantages

  • Works with a whole family of consistent products.
  • The client depends on abstract interfaces, not concrete classes.
  • Swapping a family (say, a vendor) leaves client code untouched.
  • Guarantees that products created together are compatible.

When to use

  • When a system must be independent of how its products are created.
  • When there are several products that form consistent families.
  • When whole sets of products must be switched at runtime.
  • When products of one family must be used together.

Usage in Laravel

In Laravel an abstract factory is a driver manager: Illuminate\Cache\CacheManager, Illuminate\Filesystem\FilesystemManager, Illuminate\Queue\QueueManager. Each creates not a single object but a consistent family of implementations (a cache store, a disk, a queue) chosen by the driver name from configuration. Through Cache::store('redis') or Storage::disk('s3') the client gets the right family without knowing the concrete classes.