patron-factory-method

Factory Method Pattern: Creation Through Subclasses

  • 5 min

The Factory Method is a creation method whose implementation can be decided by subclasses. It allows a class’s logic to work with an abstract product without knowing the concrete class it builds.

Sounds harmless, right? You need an object, you do new MyObject() and you’re done. But what happens when your application grows and suddenly that object is no longer enough? What if you need to decide which object to create at runtime based on configuration or environment?

If your code is littered with new Truck(), new Car(), new Bicycle(), every time you want to add a new vehicle you’ll have to open and modify all the classes that use them. That violates the SOLID Open/Closed Principle (OCP).

This is where the Factory Method comes to the rescue.

The Problem: A Logistics System

Imagine you are creating an application for a logistics company. At first, the company only delivers by road, so you create a Truck class.

public class Logistics
{
    public void PlanDelivery()
    {
        var truck = new Truck(); // ❌ Tight coupling
        truck.Deliver();
    }
}
Copied!

Everything works fine. But the company grows and starts delivering by sea. Now you need ships.

If you stick with the same design, you’ll have to fill your Logistics class with ugly conditionals:

if (type == "road") {
    transport = new Truck();
} else if (type == "sea") {
    transport = new Ship();
}
Copied!

This is unsustainable. Every time you add a new type of transport (plane, train, drone…), you have to modify the Logistics class, risking breaking code that already worked.

The Solution: Factory Method

The Factory Method pattern suggests that instead of calling the new operator directly, we invoke a special factory method.

The key idea is:

We define a common interface for all products (e.g., ITransport).

We create a creator class (Creator) that declares the factory method CreateTransport().

We let subclasses implement that method and decide which concrete class to instantiate.

:::

Define an interface for creating an object, but let subclasses decide which class to instantiate. Lets a class defer instantiation to subclasses.

Implementation in C#

Let’s refactor our logistics example using the pattern.

The Products (What we build)

First, we define what behavior all our transports have in common.

// Common interface
public interface ITransport
{
    void Deliver();
}

// Concrete products
public class Truck : ITransport
{
    public void Deliver()
    {
        Console.WriteLine("📦 Delivery by road in a box.");
    }
}

public class Ship : ITransport
{
    public void Deliver()
    {
        Console.WriteLine("🚢 Delivery by sea in a container.");
    }
}
Copied!

The Creator (The Factory)

This is where the magic happens. The Logistics class no longer knows what transport it’s creating. It only knows it will receive “something” that implements ITransport.

public abstract class Logistics
{
    // THE FACTORY METHOD
    // It's abstract, forcing subclasses to implement it
    public abstract ITransport CreateTransport();

    // Business logic
    // Notice this class doesn't know if it works with trucks or ships
    public void PlanDelivery()
    {
        // We call the factory method to get the object
        ITransport transport = CreateTransport();
        
        // We use the object (Pure polymorphism)
        transport.Deliver();
    }
}
Copied!

The Concrete Creators

Now we create the specific versions of logistics for each mode of transport.

public class RoadLogistics : Logistics
{
    public override ITransport CreateTransport()
    {
        return new Truck();
    }
}

public class SeaLogistics : Logistics
{
    public override ITransport CreateTransport()
    {
        return new Ship();
    }
}
Copied!

How to Use This?

The client code (our Program.cs or controller) works with the abstract class Logistics, without caring which concrete implementation it is using.

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("--- Client: I want road shipping ---");
        RunClient(new RoadLogistics());

        Console.WriteLine("\n--- Client: I want sea shipping ---");
        RunClient(new SeaLogistics());
    }

    // The client works with the abstraction 'Logistics'
    static void RunClient(Logistics logistics)
    {
        logistics.PlanDelivery();
    }
}
Copied!

Output:

--- Client: I want road shipping ---
📦 Delivery by road in a box.

--- Client: I want sea shipping ---
🚢 Delivery by sea in a container.
Copied!

What Have We Gained?

Notice the power of this. If tomorrow we want to add Air Logistics:

  1. Create a Plane class that implements ITransport.
  2. Create an AirLogistics class that extends Logistics and returns new Plane().

We don’t modify Logistics or the other implementations. The point where we choose the concrete logistics will indeed need to know about the new option, unless that selection is resolved through configuration or dynamic registration.

When to Use Factory Method

  • When you don’t know in advance what exact types of objects your code will need.
  • When you want to provide users of your library a way to extend its internal components.
  • To save resources: Sometimes the factory method doesn’t create a new object every time, but recycles an existing one (Pooling) or returns a cached instance. By having the control in one method, you can put that logic in there without the client knowing.

The Factory Method is the “father” of creational patterns. It teaches us to stop depending on concrete classes and start depending on abstractions.