Menu
Dev.to #architecture·October 19, 2026

Applying the Factory Method Pattern in Software Architecture

This article explores the Factory Method design pattern, illustrating its use in managing object creation for different payment methods within an e-commerce system. It covers the classic GoF definition, a pragmatic 'simple factory' variant, and modern .NET keyed services for dynamic dependency injection, highlighting how this pattern enhances flexibility and maintainability in software architecture.

Read original on Dev.to #architecture

The Factory Method pattern is a creational design pattern that addresses the problem of creating objects without specifying the exact class of object that will be created. It achieves this by defining an interface for creating an object, but letting subclasses decide which class to instantiate. This pattern is crucial for building flexible and maintainable systems, especially when dealing with multiple concrete implementations of an interface, such as different payment processors in an e-commerce application.

The Problem: Rigid Object Creation

Without patterns like Factory Method, code often becomes tightly coupled to concrete implementations. For example, processing payments might involve `if-else` or `switch` statements that directly instantiate `CreditCardProcessor`, `PayPalProcessor`, etc. This approach leads to code that is difficult to maintain, hard to extend when new payment methods are added, and violates the Open/Closed Principle.

csharp
public void ProcessPayment(string paymentType, decimal amount) {
    if (paymentType == "CreditCard") {
        var processor = new CreditCardProcessor();
        processor.Process(amount);
    }
    else if (paymentType == "PayPal") {
        var processor = new PayPalProcessor();
        processor.Process(amount);
    }
    // ... many more payment methods, leading to rigid code
}

Classic Factory Method (Gang of Four)

The traditional Factory Method pattern separates the 'product' (e.g., `IPaymentProcessor`) from its 'creator'. It involves four main roles:

  • Product: An interface (e.g., `IPaymentProcessor`) for the objects the factory method creates.
  • Concrete Products: Specific implementations of the Product interface (e.g., `CreditCardProcessor`).
  • Creator: An abstract class or interface declaring the factory method (e.g., `CreateProcessor()`). It may also define an operation that uses the factory method.
  • Concrete Creators: Subclasses that implement the factory method to return a specific Concrete Product (e.g., `CreditCardProcessorCreator` returns `CreditCardProcessor`).
💡

Architectural Benefit

This structure promotes loose coupling, allowing the client code to work with the `Product` interface rather than concrete implementations. It makes the system more extensible, as new concrete products and creators can be added without modifying existing client code.

Pragmatic Variations and Modern Implementations

While the classic pattern provides robust decoupling, it can lead to a proliferation of classes (one creator per product). Pragmatic variations, like a simple factory with a static method, centralize the creation logic using conditional statements (e.g., switch expressions in C#). Modern dependency injection containers, especially with features like keyed services (.NET 8+), offer another powerful way to implement factory-like behavior by registering multiple implementations of an interface under different keys, allowing for flexible resolution at runtime without explicit factory classes for each product.

design patternsfactory methoddependency injectionC#e-commercesoftware architectureobject-oriented design.NET

Comments

Loading comments...