Skip to content

Factory Method (Creational)

Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created.

Imagine that you’re creating a logistics management application. The first version of your app can only handle transportation by trucks, so the bulk of your code lives inside the Truck class.

After a while, your app becomes pretty popular. Each day you receive dozens of requests from sea transportation companies to incorporate sea logistics into the app.

Great news, right? But what about the code? At present, most of your code is coupled to the Truck class. Adding Ships into the app would require making changes to the entire codebase. Moreover, if later you decide to add another type of transportation to the app, you will probably need to make all of these changes again.

As a result, you will end up with some pretty nasty code, riddled with conditionals that switch the app’s behavior depending on the class of transportation objects.

The Factory Method pattern suggests that you replace direct object construction calls (using the new operator) with calls to a special factory method. Don’t worry: the objects are still created via the new operator, but it’s being called from within the factory method. Objects returned by a factory method are often referred to as products.

At first glance, this change may look pointless: we just moved the constructor call from one part of the program to another. However, consider this: now you can override the factory method in a subclass and change the class of products being created by the method.

There’s a slight limitation though: subclasses may return different types of products only if these products have a common base class or interface. Also, the factory method in the base class should have its return type declared as this interface.

Imagine a large delivery company. In the beginning, they only deliver mail using bikes. As the company grows, they start using trucks and eventually planes.

The β€œFactory” here is the Dispatcher. When a package needs to be sent, the dispatcher doesn’t just grab a random vehicle; they follow a process (the Factory Method) to assign the right β€œProduct” (Bike, Truck, or Plane) based on the destination and size, without the customer needing to know which specific vehicle is carrying their letter.

  1. Product declares the interface, which is common to all objects that can be produced by the creator and its subclasses.
  2. Concrete Products are different implementations of the product interface.
  3. Creator class declares the factory method that returns new product objects. It’s important that the return type of this method matches the product interface.
  4. Concrete Creators override the base factory method so it returns a different type of product.
classDiagram
    class Creator {
        <<abstract>>
        +someOperation()
        +createProduct() Product*
    }
    class ConcreteCreatorA {
        +createProduct() Product
    }
    class ConcreteCreatorB {
        +createProduct() Product
    }
    class Product {
        <<interface>>
        +doStuff()
    }
    class ConcreteProductA {
        +doStuff()
    }
    class ConcreteProductB {
        +doStuff()
    }

    Creator <|-- ConcreteCreatorA
    Creator <|-- ConcreteCreatorB
    Product <|.. ConcreteProductA
    Product <|.. ConcreteProductB
    ConcreteCreatorA ..> ConcreteProductA : creates
    ConcreteCreatorB ..> ConcreteProductB : creates
# The product interface declares the operations that all
# concrete products must implement.
interface Transport:
method deliver()
# Concrete products provide various implementations of the
# product interface.
class Truck implements Transport:
method deliver():
print("Deliver by land in a box.")
class Ship implements Transport:
method deliver():
print("Deliver by sea in a container.")
# The Creator class declares the factory method that is
# supposed to return an object of a Product class.
abstract class Logistics:
abstract method createTransport(): Transport
# Note that the Creator's primary responsibility isn't
# creating products. It usually contains some core
# business logic that relies on Product objects.
method planDelivery():
Transport t = createTransport()
t.deliver()
# Concrete creators override the factory method to change
# the resulting product's type.
class RoadLogistics extends Logistics:
method createTransport():
return new Truck()
class SeaLogistics extends Logistics:
method createTransport():
return new Ship()
  • Use the Factory Method when you don’t know beforehand the exact types and dependencies of the objects your code should work with. The Factory Method separates product construction code from the code that actually uses the product. Therefore it’s easier to extend the product construction code independently from the rest of the code.
  • Use the Factory Method when you want to provide users of your library or framework with a way to extend its internal components. Inheritance is probably the easiest way to extend the default behavior of a library or framework. But how would the framework recognize that your subclass should be used instead of a standard component? The solution is to reduce the code that constructs components across the framework into a single factory method and let anyone override this method in addition to extending the component itself.
  • Use the Factory Method when you want to save system resources by reusing existing objects instead of rebuilding them each time. This is common when dealing with large, resource-intensive objects like database connections, file systems, and network resources.
  1. Make all products follow the same interface. This interface should declare methods that make sense in every product.
  2. Add an empty factory method inside the creator class. The return type of the method should match the common product interface.
  3. In the creator’s code, find all references to product constructors. One by one, replace them with calls to the factory method, while extracting the product creation code into the factory method.
  4. Now, create a set of creator subclasses for each type of product listed in the factory method. Override the factory method in the subclasses and extract the appropriate bits of construction code from the base method.

Pros:

  • You avoid tight coupling between the creator and the concrete products.
  • Single Responsibility Principle. You can move the product creation code into one place in the program, making the code easier to support.
  • Open/Closed Principle. You can introduce new types of products into the program without breaking existing client code.

Cons:

  • The code may become more complicated since you need to introduce a lot of new subclasses to implement the pattern. The best-case scenario is when you’re introducing the pattern into an existing hierarchy of creator classes.
  • Many designs start by using Factory Method (less complicated and more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complicated).
  • Abstract Factory classes are often based on a set of Factory Methods, but you can also use Prototype to compose the methods on these classes.
  • Factory Method is a specialization of Template Method. At the same time, a Factory Method may serve as a step in a large Template Method.

// Product
public interface IProduct
{
void Execute();
}
// Concrete Products
public class ProductA : IProduct
{
public void Execute() => Console.WriteLine("Product A");
}
public class ProductB : IProduct
{
public void Execute() => Console.WriteLine("Product B");
}
// Creator
public abstract class Creator
{
public abstract IProduct FactoryMethod();
public void Run()
{
var product = FactoryMethod();
product.Execute();
}
}
// Concrete Creators
public class CreatorA : Creator
{
public override IProduct FactoryMethod() => new ProductA();
}
public class CreatorB : Creator
{
public override IProduct FactoryMethod() => new ProductB();
}
// Usage
class Program
{
static void Main()
{
Creator creator = new CreatorA();
creator.Run();
creator = new CreatorB();
creator.Run();
}
}