Marcelo Pastorino, Software Developer

Factory Method and Abstract Factory

Creational design patterns that delegate object construction to subclasses or factory families, so clients depend on abstractions instead of concrete types.

Factory Method and Abstract Factory are creational design patterns that move object creation behind abstractions. Instead of calling new on a concrete class, the client asks a factory for an object and receives something that implements a shared interface. The difference is scope: Factory Method creates one product type through a method overridden in subclasses; Abstract Factory creates whole families of related products through a single factory interface.

The problem they solve

When client code constructs concrete classes directly, it becomes coupled to those types. Swapping an HTML report for a PDF report, or a Windows UI control for a macOS one, means editing every place that called new. Worse, the client may need to know constructor arguments, wiring order, and which related objects belong together.

Factories push that knowledge into dedicated creation code. The client depends on interfaces and factory abstractions, and concrete classes are chosen in one place, often at the edge of the application through configuration, dependency injection, or a subclass you plug in.

Factory Method

Factory Method defines an interface for creating an object, but lets subclasses decide which class to instantiate. A base creator class contains the workflow that uses the product; it declares a factory method (often abstract or overridable) that returns the product. Each concrete creator overrides that method to return a specific concrete product.

Structure

  • Product, the interface or base type the client uses.
  • Concrete products, the classes the factory method can return.
  • Creator, declares the factory method and may contain logic that uses the product.
  • Concrete creators, override the factory method to produce a particular product.
Factory Method: subclasses choose which product to create

In code

A report generator defines the export workflow once. Subclasses supply the correct report format through a factory method.

// Products
class HtmlReport {
  render(data) {
    return `<html><body>${data}</body></html>`;
  }
}

class PdfReport {
  render(data) {
    return `PDF bytes for: ${data}`;
  }
}

// Creator with factory method
class ReportGenerator {
  createReport() {
    throw new Error("Subclass must implement createReport");
  }
  export(data) {
    const report = this.createReport();
    return report.render(data);
  }
}

// Concrete creators
class HtmlReportGenerator extends ReportGenerator {
  createReport() {
    return new HtmlReport();
  }
}

class PdfReportGenerator extends ReportGenerator {
  createReport() {
    return new PdfReport();
  }
}

const html = new HtmlReportGenerator().export("Q1 sales");
// <html><body>Q1 sales</body></html>
// Product interface
interface Report {
  render(data: string): string;
}

// Concrete products
class HtmlReport implements Report {
  render(data: string): string {
    return `<html><body>${data}</body></html>`;
  }
}

class PdfReport implements Report {
  render(data: string): string {
    return `PDF bytes for: ${data}`;
  }
}

// Creator with factory method
abstract class ReportGenerator {
  protected abstract createReport(): Report;
  export(data: string): string {
    return this.createReport().render(data);
  }
}

// Concrete creators
class HtmlReportGenerator extends ReportGenerator {
  protected createReport(): Report {
    return new HtmlReport();
  }
}

class PdfReportGenerator extends ReportGenerator {
  protected createReport(): Report {
    return new PdfReport();
  }
}

const html = new HtmlReportGenerator().export("Q1 sales");
// <html><body>Q1 sales</body></html>
// Product interface
public interface IReport
{
    string Render(string data);
}

// Concrete products
public class HtmlReport : IReport
{
    public string Render(string data) => $"<html><body>{data}</body></html>";
}

public class PdfReport : IReport
{
    public string Render(string data) => $"PDF bytes for: {data}";
}

// Creator with factory method
public abstract class ReportGenerator
{
    protected abstract IReport CreateReport();
    public string Export(string data) => CreateReport().Render(data);
}

// Concrete creators
public class HtmlReportGenerator : ReportGenerator
{
    protected override IReport CreateReport() => new HtmlReport();
}

public class PdfReportGenerator : ReportGenerator
{
    protected override IReport CreateReport() => new PdfReport();
}

var html = new HtmlReportGenerator().Export("Q1 sales");
// <html><body>Q1 sales</body></html>
from abc import ABC, abstractmethod

# Product interface (Protocol-style via ABC)
class Report(ABC):
    @abstractmethod
    def render(self, data: str) -> str: ...

# Concrete products
class HtmlReport(Report):
    def render(self, data: str) -> str:
        return f"<html><body>{data}</body></html>"

class PdfReport(Report):
    def render(self, data: str) -> str:
        return f"PDF bytes for: {data}"

# Creator with factory method
class ReportGenerator(ABC):
    @abstractmethod
    def create_report(self) -> Report: ...

    def export(self, data: str) -> str:
        return self.create_report().render(data)

# Concrete creators
class HtmlReportGenerator(ReportGenerator):
    def create_report(self) -> Report:
        return HtmlReport()

class PdfReportGenerator(ReportGenerator):
    def create_report(self) -> Report:
        return PdfReport()

html = HtmlReportGenerator().export("Q1 sales")
# <html><body>Q1 sales</body></html>

Abstract Factory

Abstract Factory provides an interface for creating families of related objects without specifying their concrete classes. A single abstract factory declares one creation method per product in the family (for example, createButton and createCheckbox). Each concrete factory implements those methods so every product it returns belongs to the same platform or theme. The client works only with abstract product types.

Structure

  • Abstract products, interfaces for each kind of object in the family.
  • Concrete products, platform- or theme-specific implementations grouped by family.
  • Abstract factory, declares creation methods for each product in the family.
  • Concrete factories, implement all creation methods for one family.
  • Client, uses only abstract factory and abstract product types.
Abstract Factory: one factory produces a matched family of products

In code

A dialog builds a form from a UI factory. Swapping the factory swaps every control to a consistent platform style.

// Abstract products
class Button {
  paint() {
    throw new Error("Subclass must implement paint");
  }
}

class Checkbox {
  paint() {
    throw new Error("Subclass must implement paint");
  }
}

// Concrete products, Windows family
class WindowsButton extends Button {
  paint() {
    return "Windows button";
  }
}

class WindowsCheckbox extends Checkbox {
  paint() {
    return "Windows checkbox";
  }
}

// Concrete products, Mac family
class MacButton extends Button {
  paint() {
    return "Mac button";
  }
}

class MacCheckbox extends Checkbox {
  paint() {
    return "Mac checkbox";
  }
}

// Abstract factory
class UIFactory {
  createButton() {
    throw new Error("Subclass must implement createButton");
  }
  createCheckbox() {
    throw new Error("Subclass must implement createCheckbox");
  }
}

// Concrete factories
class WindowsUIFactory extends UIFactory {
  createButton() {
    return new WindowsButton();
  }
  createCheckbox() {
    return new WindowsCheckbox();
  }
}

class MacUIFactory extends UIFactory {
  createButton() {
    return new MacButton();
  }
  createCheckbox() {
    return new MacCheckbox();
  }
}

// Client depends on abstractions only
function buildForm(factory) {
  const button = factory.createButton();
  const checkbox = factory.createCheckbox();
  return [button.paint(), checkbox.paint()];
}

buildForm(new WindowsUIFactory());
// ["Windows button", "Windows checkbox"]
// Abstract products
interface Button {
  paint(): string;
}

interface Checkbox {
  paint(): string;
}

// Concrete products, Windows family
class WindowsButton implements Button {
  paint(): string {
    return "Windows button";
  }
}

class WindowsCheckbox implements Checkbox {
  paint(): string {
    return "Windows checkbox";
  }
}

// Concrete products, Mac family
class MacButton implements Button {
  paint(): string {
    return "Mac button";
  }
}

class MacCheckbox implements Checkbox {
  paint(): string {
    return "Mac checkbox";
  }
}

// Abstract factory
interface UIFactory {
  createButton(): Button;
  createCheckbox(): Checkbox;
}

// Concrete factories
class WindowsUIFactory implements UIFactory {
  createButton(): Button {
    return new WindowsButton();
  }
  createCheckbox(): Checkbox {
    return new WindowsCheckbox();
  }
}

class MacUIFactory implements UIFactory {
  createButton(): Button {
    return new MacButton();
  }
  createCheckbox(): Checkbox {
    return new MacCheckbox();
  }
}

// Client depends on abstractions only
function buildForm(factory: UIFactory): string[] {
  const button = factory.createButton();
  const checkbox = factory.createCheckbox();
  return [button.paint(), checkbox.paint()];
}

buildForm(new WindowsUIFactory());
// ["Windows button", "Windows checkbox"]
// Abstract products
public interface IButton
{
    string Paint();
}

public interface ICheckbox
{
    string Paint();
}

// Concrete products, Windows family
public class WindowsButton : IButton
{
    public string Paint() => "Windows button";
}

public class WindowsCheckbox : ICheckbox
{
    public string Paint() => "Windows checkbox";
}

// Concrete products, Mac family
public class MacButton : IButton
{
    public string Paint() => "Mac button";
}

public class MacCheckbox : ICheckbox
{
    public string Paint() => "Mac checkbox";
}

// Abstract factory
public interface IUIFactory
{
    IButton CreateButton();
    ICheckbox CreateCheckbox();
}

// Concrete factories
public class WindowsUIFactory : IUIFactory
{
    public IButton CreateButton() => new WindowsButton();
    public ICheckbox CreateCheckbox() => new WindowsCheckbox();
}

public class MacUIFactory : IUIFactory
{
    public IButton CreateButton() => new MacButton();
    public ICheckbox CreateCheckbox() => new MacCheckbox();
}

// Client depends on abstractions only
string[] BuildForm(IUIFactory factory) =>
    [factory.CreateButton().Paint(), factory.CreateCheckbox().Paint()];

BuildForm(new WindowsUIFactory());
// ["Windows button", "Windows checkbox"]
from abc import ABC, abstractmethod

# Abstract products
class Button(ABC):
    @abstractmethod
    def paint(self) -> str: ...

class Checkbox(ABC):
    @abstractmethod
    def paint(self) -> str: ...

# Concrete products, Windows family
class WindowsButton(Button):
    def paint(self) -> str:
        return "Windows button"

class WindowsCheckbox(Checkbox):
    def paint(self) -> str:
        return "Windows checkbox"

# Concrete products, Mac family
class MacButton(Button):
    def paint(self) -> str:
        return "Mac button"

class MacCheckbox(Checkbox):
    def paint(self) -> str:
        return "Mac checkbox"

# Abstract factory
class UIFactory(ABC):
    @abstractmethod
    def create_button(self) -> Button: ...

    @abstractmethod
    def create_checkbox(self) -> Checkbox: ...

# Concrete factories
class WindowsUIFactory(UIFactory):
    def create_button(self) -> Button:
        return WindowsButton()

    def create_checkbox(self) -> Checkbox:
        return WindowsCheckbox()

class MacUIFactory(UIFactory):
    def create_button(self) -> Button:
        return MacButton()

    def create_checkbox(self) -> Checkbox:
        return MacCheckbox()

# Client depends on abstractions only
def build_form(factory: UIFactory) -> list[str]:
    button = factory.create_button()
    checkbox = factory.create_checkbox()
    return [button.paint(), checkbox.paint()]

build_form(WindowsUIFactory())
# ["Windows button", "Windows checkbox"]

Factory Method vs Abstract Factory

Factory Method Abstract Factory
Creates One product type per factory method A family of related product types
Mechanism Subclass overrides a method on the creator Concrete factory implements a factory interface
When to use One abstraction varies by implementation (report format, parser type) Several abstractions must stay consistent (UI controls, database + connection + query builder)
Extension Add a new creator subclass Add a new concrete factory (and its product set)

Factory Method is often the simpler step: one creator, one product hierarchy. Abstract Factory appears when the client must not mix products from different families. A Mac button with a Windows checkbox would look wrong, so both come from the same factory.

Relationship to SOLID

Both patterns apply the SOLID principles, especially dependency inversion because clients and creators depend on abstractions, not concrete classes. They also support the open/closed principle: new products or families arrive as new subclasses or factory implementations without editing the code that uses them.

In practice, dependency injection containers and module wiring often play the factory role at the application boundary, registering which concrete types satisfy which interfaces.

Factory patterns vs alternatives

  • Simple factory, a standalone function or class with a switch that returns concrete types. It is not a GoF pattern and tends to grow a central conditional; Factory Method distributes the choice across subclasses instead.
  • Builder pattern, constructs a complex object step by step when there are many optional parts or assembly order matters; factories return a ready product in one step.
  • Prototype, clones an existing instance instead of building from scratch; useful when construction is expensive or defaults are copied from a template.
  • Strategy pattern, swaps behavior on an existing object; factories create the object in the first place. Strategy’s note about pushing selection into a factory applies here: the factory picks the concrete type, the strategy (or product) carries the varying behavior.
  • Dependency injection, the container resolves abstractions at runtime; under the hood it is often a registry of factory delegates.

Trade-offs

  • More classes and indirection than calling new directly; for a single stable type used in one place, a factory is overhead.
  • Abstract Factory multiplies types quickly. Each new product in the family adds methods to every concrete factory.
  • Factory Method can hide construction but still ties the choice to inheritance; composition with a injected factory delegate or DI registration is often more flexible in application code.
  • Best when concrete types change with configuration, platform, or environment, or when construction logic (validation, wiring, pooling) should live in one place instead of at every call site.

See also

  • Builder Pattern, A creational design pattern that constructs complex objects step by step through a fluent builder, separating assembly from the product's representation.
  • Strategy Pattern, A behavioral design pattern that encapsulates interchangeable algorithms behind a common interface, selected at runtime.
  • Adapter Pattern, A structural design pattern that wraps an incompatible interface in one the client expects, letting otherwise unrelated types work together.
  • SOLID Principles, Five object-oriented design principles for maintainable, change-tolerant code.
  • Facade Pattern, A structural design pattern that exposes a single, simplified interface over a complex subsystem, hiding how its parts cooperate.

On Wikipedia