Factory Pattern

The Factory Pattern is a creational design pattern used to create objects without exposing the creation logic to the main application code. This article explains how Factory works, when to use it, and how it improves clean object-oriented design with practical PHP examples.

Jun 10, 2026
Factory Pattern

Factory Pattern

The Factory Pattern is a creational design pattern used to create objects in a clean and flexible way. Instead of creating objects directly in different parts of the application, the Factory Pattern moves object creation logic into a separate factory class or method.

This pattern is especially useful when object creation depends on conditions, configuration, user input, environment settings, or business rules. It helps keep the main application code cleaner because the code that uses an object does not need to know exactly how that object is created.

Introduction

In Object-Oriented Programming, creating objects with the new keyword is normal and simple. However, as applications grow, object creation can become more complex. A class may need different dependencies, different configuration values, or different implementations depending on the situation.

If object creation logic is repeated across controllers, services, commands, or business classes, the code becomes harder to maintain. Any change in the creation process must be updated in many places.

The Factory Pattern solves this problem by centralizing object creation. The application asks the factory to create the correct object, and the factory decides which class should be instantiated.

What Is the Factory Pattern?

The Factory Pattern is a design pattern that provides a method or class responsible for creating objects. The client code does not create the object directly. Instead, it calls the factory and receives an object that follows a known type, interface, or parent class.

In simple terms, a factory is like a production center. You request a specific type of object, and the factory returns the correct instance.

For example, an application may support multiple payment methods such as credit card, PayPal, and bank transfer. Instead of writing object creation logic inside the checkout service, a PaymentFactory can create the correct payment object based on the selected payment type.

Why the Factory Pattern Is Important

The Factory Pattern is important because it separates object creation from object usage. This makes the application easier to maintain and easier to extend.

When code creates objects directly in many places, it becomes tightly coupled to specific classes. If the class name, constructor parameters, or creation rules change, many parts of the application may need to be updated.

With a factory, the creation rules are placed in one location. The rest of the application depends on an interface or common type, not on the exact creation details.

Problem Without Factory Pattern

Imagine a checkout system that supports multiple payment methods. Without a factory, the checkout service may contain logic like this:

if ($type === 'credit_card') {
    $payment = new CreditCardPayment();
} elseif ($type === 'paypal') {
    $payment = new PayPalPayment();
} elseif ($type === 'bank_transfer') {
    $payment = new BankTransferPayment();
} else {
    throw new InvalidArgumentException('Invalid payment method.');
}

This code may work in a small example, but it becomes problematic when repeated in multiple places. If a new payment method is added, every repeated condition may need to be updated.

This also mixes two responsibilities: deciding which object to create and processing the business operation. The Factory Pattern separates these responsibilities.

Basic Factory Pattern Example in PHP

The following example shows a simple Factory Pattern implementation in PHP:

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

class CreditCardPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Process credit card payment
        return true;
    }
}

class PayPalPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Process PayPal payment
        return true;
    }
}

class BankTransferPayment implements PaymentMethod
{
    public function pay(float $amount): bool
    {
        // Process bank transfer payment
        return true;
    }
}

class PaymentFactory
{
    public static function create(string $type): PaymentMethod
    {
        return match ($type) {
            'credit_card' => new CreditCardPayment(),
            'paypal' => new PayPalPayment(),
            'bank_transfer' => new BankTransferPayment(),
            default => throw new InvalidArgumentException('Invalid payment method.'),
        };
    }
}

In this example, PaymentFactory is responsible for creating the correct payment object. The rest of the application can use the PaymentMethod interface without knowing the exact class creation logic.

Using the Factory in Application Code

After creating the factory, the checkout code becomes simpler:

$paymentMethod = PaymentFactory::create('paypal');
$paymentMethod->pay(100);

The application asks the factory for a payment method and then calls the pay method. The checkout code does not need a long conditional block.

This design makes the code cleaner and easier to extend. If a new payment method is added, the factory can be updated in one place.

Factory Pattern and Interfaces

The Factory Pattern works very well with interfaces. An interface defines what behavior the created objects must provide, while the factory decides which implementation should be returned.

In the payment example, all payment classes implement the PaymentMethod interface. This means the application can use any payment object in the same way.

This combination supports polymorphism. Different payment classes can behave differently internally, but the main code can treat them as the same type.

Factory Pattern and Polymorphism

Polymorphism allows different objects to respond to the same method in different ways. The Factory Pattern often works together with polymorphism because it returns objects that share a common interface or parent class.

For example, CreditCardPayment, PayPalPayment, and BankTransferPayment all have a pay method. The factory returns one of them, and the application calls pay without checking the exact object type.

This reduces conditional logic and helps keep the code open for extension.

Real-World Example: Notification Factory

A common real-world use case for Factory Pattern is a notification system. An application may send notifications through email, SMS, or push notification.

interface NotificationSender
{
    public function send(string $message): bool;
}

class EmailSender implements NotificationSender
{
    public function send(string $message): bool
    {
        // Send email
        return true;
    }
}

class SmsSender implements NotificationSender
{
    public function send(string $message): bool
    {
        // Send SMS
        return true;
    }
}

class PushNotificationSender implements NotificationSender
{
    public function send(string $message): bool
    {
        // Send push notification
        return true;
    }
}

class NotificationFactory
{
    public static function create(string $channel): NotificationSender
    {
        return match ($channel) {
            'email' => new EmailSender(),
            'sms' => new SmsSender(),
            'push' => new PushNotificationSender(),
            default => throw new InvalidArgumentException('Invalid notification channel.'),
        };
    }
}

This factory allows the application to create the correct notification sender based on the selected channel.

Using Notification Factory

The notification service can use the factory like this:

$sender = NotificationFactory::create('email');
$sender->send('Your order has been shipped.');

The code is simple and does not need to know how EmailSender is created internally. If SMS or push notification is selected, the same calling code can still work.

Factory Method Pattern

The Factory Method Pattern is a variation where object creation is delegated to a method that can be overridden by child classes. Instead of one standalone factory class, the creation method is part of a class hierarchy.

This is useful when a parent class defines a process, but child classes decide which object should be created during that process.

For example, a report generator may define common report generation steps, while child classes decide whether to create a PDF exporter, Excel exporter, or CSV exporter.

Factory Method Example in PHP

interface Exporter
{
    public function export(array $data): string;
}

class PdfExporter implements Exporter
{
    public function export(array $data): string
    {
        return 'PDF exported';
    }
}

class ExcelExporter implements Exporter
{
    public function export(array $data): string
    {
        return 'Excel exported';
    }
}

abstract class ReportGenerator
{
    abstract protected function createExporter(): Exporter;

    public function generate(array $data): string
    {
        $exporter = $this->createExporter();

        return $exporter->export($data);
    }
}

class PdfReportGenerator extends ReportGenerator
{
    protected function createExporter(): Exporter
    {
        return new PdfExporter();
    }
}

class ExcelReportGenerator extends ReportGenerator
{
    protected function createExporter(): Exporter
    {
        return new ExcelExporter();
    }
}

In this example, the parent class ReportGenerator defines the generate process. The child classes decide which exporter should be created.

This is useful when the creation logic depends on the subclass.

Simple Factory vs Factory Method

A simple factory is usually a separate class or static method that creates objects based on input. It is easy to understand and practical for many applications.

The Factory Method Pattern uses inheritance and allows child classes to control object creation. It is more structured and useful when the creation process is connected to a larger workflow.

Both approaches are useful. The best choice depends on the complexity of the application and the design goal.

Factory Pattern in Laravel

In Laravel applications, factory-style design can appear in several places. Laravel model factories are used to create test data. Service containers can create and resolve objects. Developers can also create custom factory classes for payment methods, notification channels, file exporters, or report generators.

For example, a Laravel application may use a PaymentFactory service that creates payment gateway objects based on configuration or user selection.

This keeps controllers clean. Instead of placing payment creation logic inside a controller, the controller can call a service that uses the factory internally.

Factory Pattern in Symfony

Symfony applications also benefit from factory classes and service configuration. A factory can create complex services, choose implementations, or prepare objects that require special construction logic.

In large Symfony projects, factories are often used with dependency injection and service definitions. This gives developers strong control over object creation while keeping business classes focused on business behavior.

When to Use the Factory Pattern

The Factory Pattern is useful when object creation is not simple or when the application needs to choose between multiple related classes.

Use the Factory Pattern when:

  • The exact class to create depends on input, configuration, or business rules.

  • Object creation logic is repeated in many places.

  • Constructors are complex or require multiple dependencies.

  • The application should depend on interfaces instead of concrete classes.

  • New implementations may be added in the future.

  • You want to separate creation logic from business logic.

Factory is especially useful in systems that support multiple providers, formats, channels, strategies, or integrations.

When Not to Use the Factory Pattern

The Factory Pattern is not always necessary. If object creation is simple and unlikely to change, adding a factory may create unnecessary complexity.

Avoid Factory Pattern when:

  • There is only one simple class to create.

  • The constructor is simple and does not need special logic.

  • The factory would only wrap new without adding real value.

  • The project is very small and does not need extra abstraction.

  • The pattern makes the code harder to understand.

Good design patterns should simplify the code. If a factory adds more confusion than clarity, it may not be needed.

Benefits of Factory Pattern

The Factory Pattern provides many practical benefits in object-oriented software design.

Main benefits include:

  • Centralizes object creation logic.

  • Reduces duplicated creation code.

  • Separates business logic from construction logic.

  • Supports polymorphism and interface-based design.

  • Makes adding new implementations easier.

  • Improves maintainability in medium and large projects.

  • Can make testing easier when combined with dependency injection.

These benefits make the Factory Pattern one of the most useful design patterns for real-world applications.

Factory Pattern and Clean Code

The Factory Pattern supports clean code by keeping object creation out of places where it does not belong. Controllers, services, and domain classes should usually focus on their main responsibility, not on complex object construction rules.

For example, a checkout service should process checkout behavior. It should not contain long blocks of code that decide how to create every payment provider.

Moving creation logic into a factory makes the code easier to read and easier to change.

Factory Pattern and Open Closed Principle

The Factory Pattern can support the Open Closed Principle when designed carefully. The Open Closed Principle says that software should be open for extension but closed for modification.

With a factory, new object types can often be added with limited changes to the application. For example, adding a new payment class may require updating the factory, while the checkout service remains unchanged.

In more advanced designs, factories can use configuration, service containers, or maps to reduce direct modifications even further.

Factory Pattern with Configuration Map

Instead of using a long match or switch statement, a factory can use a configuration map that connects keys to class names.

class PaymentFactory
{
    private array $methods = [
        'credit_card' => CreditCardPayment::class,
        'paypal' => PayPalPayment::class,
        'bank_transfer' => BankTransferPayment::class,
    ];

    public function create(string $type): PaymentMethod
    {
        if (!isset($this->methods[$type])) {
            throw new InvalidArgumentException('Invalid payment method.');
        }

        return new $this->methods[$type]();
    }
}

This approach can make the factory easier to extend, especially when the list of implementations grows.

Factory Pattern with Dependency Injection

In real applications, created objects may need dependencies such as API clients, loggers, configuration objects, or database connections. In that case, the factory can receive dependencies through its constructor and pass them to the objects it creates.

class PaymentFactory
{
    public function __construct(
        private LoggerInterface $logger
    ) {
    }

    public function create(string $type): PaymentMethod
    {
        return match ($type) {
            'credit_card' => new CreditCardPayment($this->logger),
            'paypal' => new PayPalPayment($this->logger),
            default => throw new InvalidArgumentException('Invalid payment method.'),
        };
    }
}

This design is better than using static methods when the factory itself needs dependencies. It also makes the factory easier to test.

Static Factory Methods

Some factories use static methods for simple object creation. Static factory methods can be convenient when no dependencies are needed.

However, static factories can become harder to test when the creation process becomes complex. If the factory needs dependencies or configuration, an object-based factory is usually better.

As a general rule, static factory methods are acceptable for simple cases, while dependency-injected factory services are better for larger applications.

Common Mistakes with Factory Pattern

One common mistake is creating a factory for every class, even when object creation is simple. This adds unnecessary files and makes the project harder to navigate.

Another mistake is putting too much business logic inside the factory. A factory should focus on creating objects. Business rules should usually stay in services, domain classes, or use cases.

A third mistake is returning unrelated objects from the same factory. A factory should create objects that belong to the same family or follow the same interface.

A fourth mistake is using a long switch statement that grows too much without considering better organization. If the factory becomes too large, it may need maps, separate factories, or a service container.

Best Practices for Factory Pattern

To use the Factory Pattern correctly, developers should keep the design focused and practical.

Useful best practices include:

  • Create factories only when object creation has real complexity.

  • Return interfaces or parent types when possible.

  • Keep factory classes focused on creation logic.

  • Avoid placing business workflows inside factories.

  • Use dependency injection for factories that need services.

  • Use clear names such as PaymentFactory or NotificationFactory.

  • Keep related object types in the same factory.

  • Refactor large factories when they become difficult to maintain.

These practices help keep the Factory Pattern clean and useful.

Factory Pattern vs Strategy Pattern

The Factory Pattern and Strategy Pattern are different, but they can work together. The Factory Pattern focuses on creating objects. The Strategy Pattern focuses on choosing and using interchangeable behavior.

For example, a factory may create a discount strategy based on customer type. The created strategy then calculates the discount. In this case, Factory handles creation, while Strategy handles behavior.

Understanding the difference helps developers choose the right pattern for the right problem.

Factory Pattern vs Singleton Pattern

The Singleton Pattern controls the number of instances and ensures only one instance exists. The Factory Pattern controls how objects are created and may create different instances or different classes based on the situation.

Singleton is about a single shared object. Factory is about flexible object creation.

In modern applications, Factory Pattern is often safer and more flexible than Singleton because it does not necessarily introduce global state.

Practical Checklist Before Using Factory Pattern

Before using a factory, developers can ask these questions:

  • Is object creation repeated in several places?

  • Does the class to create depend on input or configuration?

  • Do the created classes share the same interface?

  • Will new implementations be added later?

  • Will a factory make the main code cleaner?

  • Is object creation complex enough to deserve a factory?

  • Can dependency injection or a service container help?

If the answer is yes to several of these questions, the Factory Pattern may be a good design choice.

Conclusion

The Factory Pattern is a creational design pattern that centralizes object creation and separates it from the main business logic. It helps developers create objects based on input, configuration, or business rules without spreading creation logic across the application.

Factory Pattern works especially well with interfaces and polymorphism. It improves maintainability, reduces duplication, and makes applications easier to extend when new implementations are added.

However, Factory should be used only when it solves a real problem. Simple object creation does not always need a factory. When applied correctly, the Factory Pattern is one of the most practical and useful design patterns for clean Object-Oriented Programming and real-world software development.