Design Patterns, Explained Simply · Creational Patterns: Creating Objects Flexibly

Factory Method

Let subclasses decide which concrete object to create, so the code that uses the object never names its class.

The problem

A reporting feature exports rows as CSV. Then customers want JSON. Then XML. The export() method has if format == "csv" checks in three places: building the header, writing each row, and joining the output. Each new format means finding and editing every one of those checks.

The real issue is that export() both decides which formatter to use and uses it. Those two jobs change for different reasons.

Real-world analogy

A coffee chain has one standard way to serve a drink: take the order, make the drink, hand it over. Which machine makes the drink is up to each branch: an espresso bar uses an espresso machine, a kiosk uses a capsule machine. Head office writes the serving process once; each branch decides what to "create".

Without the pattern

def export(rows, fmt):
    lines = []
    for row in rows:
        if fmt == "csv":
            lines.append(f"{row['id']},{row['name']}")
        elif fmt == "json":
            lines.append(f'{{"id": {row["id"]}, "name": "{row["name"]}"}}')
        # every new format: another branch here, and in any other place that checks fmt
    return "\n".join(lines)

The pattern

  • Creator (Report): has the shared algorithm (export()) and declares a factory method (create_formatter()) that returns a product.
  • Concrete creators (CsvReport, JsonReport): override the factory method to return a specific product.
  • Product (Formatter): the interface the creator uses (format(row)).
  • Concrete products (CsvFormatter, JsonFormatter).

The creator only talks to the product interface, so adding XML means adding XmlReport and XmlFormatter. export() doesn't change.

The code

Each report subclass decides which formatter to createTry it yourself
class CsvFormatter:
    def format(self, row):
        return f"{row['id']},{row['name']}"


class JsonFormatter:
    def format(self, row):
        return f'{{"id": {row["id"]}, "name": "{row["name"]}"}}'


class Report:
    def __init__(self, rows):
        self.rows = rows

    def create_formatter(self):  # the factory method
        raise NotImplementedError

    def export(self):  # shared algorithm, never names a concrete formatter
        formatter = self.create_formatter()
        return "\n".join(formatter.format(row) for row in self.rows)


class CsvReport(Report):
    def create_formatter(self):
        return CsvFormatter()


class JsonReport(Report):
    def create_formatter(self):
        return JsonFormatter()


rows = [{"id": 1, "name": "Ada"}, {"id": 2, "name": "Linus"}]
for report in [CsvReport(rows), JsonReport(rows)]:
    print(f"-- {type(report).__name__}")
    print(report.export())

Output

-- CsvReport
1,Ada
2,Linus
-- JsonReport
{"id": 1, "name": "Ada"}
{"id": 2, "name": "Linus"}

A simpler cousin you will see more often is the simple factory: one function like create_formatter("csv") that returns the right object. It isn't one of the 23 GoF patterns, but it solves the same "who decides which class" problem without subclasses.

When to use it

  • The shared workflow is fixed, but which object it works with varies by subclass, configuration or platform.
  • You want to add new product types without editing the code that uses them (Open/Closed).
  • A framework needs to let users plug in their own objects (a base class with an overridable create_* hook).

When not to use it

  • There is only one product type and no sign of a second. A plain new is clearer.
  • A simple factory function or a dictionary from name to class is enough. You don't need a subclass per product.
  • In languages with first-class functions, passing the formatter (or a function that makes one) to export() is often simpler than subclassing.

You've already used it

  • Java: Collection.iterator() is a factory method. Each collection returns its own iterator type.
  • Python: logging.getLogger() and open(), which picks a text or binary file object based on the mode.
  • Django: get_form_class() in generic views and get_queryset() are overridable factory methods.
  • Laravel: a model's newCollection() and newEloquentBuilder() let subclasses supply custom objects.

Key points

  • A factory method is an overridable method that creates the object a shared algorithm needs.
  • The algorithm talks only to the product interface, so new products need no edits to it.
  • The simple factory (one creating function) is the everyday version; Factory Method adds subclass control.
  • Further reading: Refactoring Guru: Factory Method.

Practice questions

What problem does the Factory Method pattern solve?

Answer

It separates deciding which object to create from using it. The base class has a shared algorithm that calls an overridable factory method; subclasses return different concrete products. New products can be added without editing the algorithm.

What is the difference between a simple factory and the Factory Method pattern?

Answer

A simple factory is one function or class with a conditional that returns the right object (create_formatter('csv')). Factory Method uses inheritance: each subclass overrides the creating method. The simple factory is more common; Factory Method suits frameworks where users extend a base class.

Give an example of Factory Method in a standard library.

Answer

Java's Collection.iterator(): every collection class returns its own Iterator implementation, and code that loops over collections only depends on the Iterator interface.

Open in the app to mark lessons complete, take notes and track your progress.