Skip to content

Simple Factory

The Simple Factory Pattern is a common programming idiom where a single factory class is responsible for creating objects based on input parameters.

Not a GoF Pattern

Simple Factory is not one of the 23 Gang of Four design patterns. It is widely used in practice because it centralizes object creation logic and hides the concrete class instantiation from the client.


Structure

Role Example Responsibility
Factory PaymentFactory Decides which concrete product to create
Product PaymentMethod Defines the common interface
Concrete Product PaypalPayment, StripePayment Implements the product interface

Steps

  1. Create a Product interface
  2. Implement Concrete Products
  3. Create a Factory class
  4. The factory decides which object to create based on input
classDiagram
    class PaymentFactory {
        +create(method): PaymentMethod
    }

    class PaymentMethod {
        <<interface>>
        +pay(amount)
    }

    class PaypalPayment {
        +pay(amount)
    }

    class StripePayment {
        +pay(amount)
    }

    PaymentMethod <|.. PaypalPayment
    PaymentMethod <|.. StripePayment
    PaymentFactory --> PaymentMethod : creates

The client calls the factory (like PaymentFactory) with a parameter (for example "Paypal" or "Stripe"), and the factory decides which concrete product (like Paypal or Stripe) should be instantiated.


Example 1: Payment Factory

PaymentFactory creates the appropriate payment gateway based on a payment method identifier.


Product Interface

PaymentMethod.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Payment;

interface PaymentMethod
{
    public function pay(float $amount): string;
}

Factory

PaymentFactory.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Payment;

class PaymentFactory
{
    /**
     * @param string $payment
     * @return PaymentMethod
     * @throws \InvalidArgumentException
     */
    public function create(string $payment): PaymentMethod
    {
        return match (strtolower($payment)) {
            'paypal' => new PaypalPayment(),
            'stripe' => new StripePayment(),
            default => throw new \InvalidArgumentException('Payment Method not created'),
        };
    }
}

Concrete Products

PaypalPayment.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Payment;

class PaypalPayment implements PaymentMethod
{
    public function pay(float $amount): string
    {
        return "Paid $amount via PayPal";
    }
}
StripePayment.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Payment;

class StripePayment implements PaymentMethod
{
    public function pay(float $amount): string
    {
        return "Paid $amount via Stripe";
    }
}

Tests

PaymentTest.php
<?php

declare(strict_types=1);

namespace Tests\Creational\SimpleFactory;

use DesignPattern\Creational\SimpleFactory\Payment\PaymentFactory;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;

#[CoversClass(PaymentFactory::class)]
class PaymentTest extends TestCase
{
    /**
     * @return void
     * @throws \InvalidArgumentException
     */
    public function testFactoryMethod(): void
    {
        $paymentMethod = new PaymentFactory();
        $paypal = $paymentMethod->create('paypal');
        $stripe = $paymentMethod->create('stripe');

        self::assertSame('Paid 100 via PayPal', $paypal->pay(100));
        self::assertSame('Paid 50 via Stripe', $stripe->pay(50));
    }
}

Example 2: Animal Factory

AnimalFactory creates different Animal objects based on a given type.


Product Interface

Animal.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Animal;

interface Animal
{
    public function speak(): string;
}

Factory

AnimalFactory.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Animal;

class AnimalFactory
{
    /**
     * @param string $animal
     * @return Animal
     * @throws \InvalidArgumentException
     */
    public function create(string $animal): Animal
    {
        return match (strtolower($animal)) {
            'cat' => new Cat(),
            'dog' => new Dog(),
            default => throw new \InvalidArgumentException('Animal ' . $animal . ' not created'),
        };
    }
}

Concrete Products

Dog.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Animal;

class Dog implements Animal
{
    public function speak(): string
    {
        return 'woof';
    }
}
Cat.php
<?php

declare(strict_types=1);

namespace DesignPattern\Creational\SimpleFactory\Animal;

class Cat implements Animal
{
    public function speak(): string
    {
        return 'miau';
    }
}

Tests

Cat.php
<?php

declare(strict_types=1);

namespace Tests\Creational\SimpleFactory;

use DesignPattern\Creational\SimpleFactory\Animal\AnimalFactory;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\TestCase;

#[CoversClass(AnimalFactory::class)]
class AnimalTest extends TestCase
{
    /**
     * @return void
     * @throws \InvalidArgumentException
     */
    public function testFactoryMethod(): void
    {
        $factory = new AnimalFactory();
        $cat = $factory->create('cat');
        $dog = $factory->create('dog');

        self::assertSame('miau', $cat->speak());
        self::assertSame('woof', $dog->speak());
    }
}