Overview

The Factory pattern is a Creational design pattern that provides an interface for creating objects but allows subclasses to alter the type of objects that will be created. It abstracts the instantiation process, so the client code doesn't need to know the details of how objects are created.

Instead of having multiple places in your code that instantiate a specific class, the Factory Pattern centralises the logic. If you need to change the implementation, you do so in one place without touching the rest of the codebase.

Example

Here is a code example using the creation of payment gateway objects as an example. First, lets define an interface that payment gateway classes must adhere to:

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

With that in place, lets create some concrete classes for different payment gateways:

class PayPalGateway implements PaymentGateway
{
    public function pay(float $amount): string
    {
        return "Charge $amount with Paypal";
    }
}

class SquareGateway implements PaymentGateway
{
    public function pay(float $amount): string
    {
        return "Charge $amount with Square";
    }
}

class StripeGateway implements PaymentGateway
{
    public function pay(float $amount): string
    {
        return "Charge $amount with Stripe";
    }
}

And finally, lets create a factory which returns one of the above concrete implementations:

class PaymentGatewayFactory
{
    public function make(string $gatewayType): PaymentGateway
    {
        return match (strtolower($gatewayType)) {
            'paypal' => new PayPalGateway(),
            'stripe' => new StripeGateway(),
            'square' => new SquareGateway(),
            default => throw new InvalidArgumentException("Unsupported gateway type: $gatewayType"),
        };
    }
}

The make function accepts a string, this string determines which object is created and returned.

But how do we put this together? Here we use the Factory Pattern to dynamically create and use different payment gateways.

$factory = new PaymentGatewayFactory();
$gateway = $factory->make('square');
$gateway->pay(75); // Charge 75 with Square

Advantages

  • Decoupling: The client code doesn’t need to know the specifics of object creation.

  • Single Responsibility: Object creation logic is encapsulated in the factory, making your code more maintainable.

  • Scalability: Adding new gateways (or products) is straightforward with minimal impact on the client code.

Disadvantages

  • Increased complexity: For simple use cases, introducing a factory might add unnecessary layers.

  • Maintenance overhead: New types of objects require updates to the factory logic.

Want to see the Factory Pattern in action? Check out this simple PHP payment gateway example: Github