PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

Factory Method and Abstract Factory – implementations, differences table, Simple Factory

by Henryk Tews / Tuesday, 10 May 2022 / Published in Wzorce projektowe

Factory Method and Abstract Factory are both creational patterns that remove the new keyword from business logic – but they solve different problems. Factory Method delegates creation to subclasses. Abstract Factory creates families of related objects. I implement both from scratch, compare them in a table, and show where Simple Factory fits as a pragmatic alternative.

Factory Method – subclass decides what to create

<?php

declare(strict_types=1);

// The "factory method" is an abstract method in the base class
abstract class Notification
{
    // Factory Method - subclasses define how to create the sender
    abstract protected function createSender(): SenderInterface;

    // Template method uses the factory method
    public function send(string $recipient, string $message): void
    {
        $sender = $this->createSender();
        $sender->send($recipient, $message);
    }
}

interface SenderInterface
{
    public function send(string $recipient, string $message): void;
}

class EmailNotification extends Notification
{
    protected function createSender(): SenderInterface
    {
        return new SmtpSender(host: 'smtp.example.com', port: 587);
    }
}

class SmsNotification extends Notification
{
    protected function createSender(): SenderInterface
    {
        return new TwilioSender(accountSid: 'ACxxxx', authToken: 'xxxx');
    }
}

// Client code works with Notification - doesn't know which sender is used
function notifyCustomer(Notification $notification, string $email): void
{
    $notification->send($email, 'Your order is ready.');
}

notifyCustomer(new EmailNotification(), 'customer@example.com');
notifyCustomer(new SmsNotification(), '+48600100200');

Abstract Factory – families of related objects

<?php

declare(strict_types=1);

// Abstract Factory creates a FAMILY of related objects
// Here: UI components for different themes

interface ButtonInterface   { public function render(): string; }
interface FormInterface     { public function render(): string; }
interface TableInterface    { public function render(): string; }

// Abstract Factory - interface for creating the whole family
interface UiComponentFactory
{
    public function createButton(string $label): ButtonInterface;
    public function createForm(array $fields): FormInterface;
    public function createTable(array $columns): TableInterface;
}

// Concrete factory 1: Bootstrap theme components
class BootstrapUiFactory implements UiComponentFactory
{
    public function createButton(string $label): ButtonInterface
    {
        return new BootstrapButton($label);
    }
    public function createForm(array $fields): FormInterface
    {
        return new BootstrapForm($fields);
    }
    public function createTable(array $columns): TableInterface
    {
        return new BootstrapTable($columns);
    }
}

// Concrete factory 2: Tailwind theme components
class TailwindUiFactory implements UiComponentFactory
{
    public function createButton(string $label): ButtonInterface
    {
        return new TailwindButton($label);
    }
    public function createForm(array $fields): FormInterface
    {
        return new TailwindForm($fields);
    }
    public function createTable(array $columns): TableInterface
    {
        return new TailwindTable($columns);
    }
}

// Application uses the factory without knowing the concrete classes
class AdminPanel
{
    public function __construct(private UiComponentFactory $ui) {}

    public function render(): string
    {
        $table = $this->ui->createTable(['Name', 'SKU', 'Price']);
        $btn   = $this->ui->createButton('Add Product');
        return $table->render() . $btn->render();
    }
}

// Swap the entire theme by changing one line
$panel = new AdminPanel(new TailwindUiFactory());
echo $panel->render();

Simple Factory – the pragmatic alternative

<?php

// Simple Factory is not a GoF pattern - but it is often the right choice
// Use it when you do not need subclass polymorphism or object families

class NotifierFactory
{
    public function create(string $channel): SenderInterface
    {
        return match($channel) {
            'email' => new SmtpSender(),
            'sms'   => new TwilioSender(),
            'slack' => new SlackSender(),
            default => throw new \InvalidArgumentException("Unknown channel: {$channel}"),
        };
    }
}

// With DI for extensibility - Magento 2 style
class ExtensibleNotifierFactory
{
    public function __construct(
        private array $senders // injected via di.xml
    ) {}

    public function create(string $channel): SenderInterface
    {
        return $this->senders[$channel]
            ?? throw new \InvalidArgumentException("Unknown channel: {$channel}");
    }
}

Comparison table

Aspect Simple Factory Factory Method Abstract Factory
GoF pattern? No Yes Yes
Mechanism Single class Inheritance Composition
Creates One product type One product type Family of products
Extension Modify factory New subclass New factory class
Complexity Low Medium High
When to use Simple creation logic Polymorphic creation UI kits, cross-platform code

Summary

Start with Simple Factory. Upgrade to Factory Method when you find yourself extending the factory class hierarchy rather than modifying it. Use Abstract Factory when you need to ensure consistent object families – theme components, database drivers, platform-specific implementations. In Magento 2 the auto-generated *Factory classes are Simple Factories with DI-injected configuration, giving extensibility without full Abstract Factory complexity.

About Henryk Tews

What you can read next

Observer pattern in PHP and the Magento 2 event system
Adapter and Facade in PHP – structural patterns
Singleton and Builder in PHP – creational patterns

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}