Back to Creational: Singleton, Factory, Builder, Prototype
Q2Question 2 of 12

Explain Factory Method, Abstract Factory, Builder, Prototype, and when each is used

Key Concepts
  • Factory Method picks one concrete product behind a method; caller never writes new.
  • Abstract Factory produces a whole matching family of related products through a factory interface.
  • Builder assembles a complex object step by step with chained setters and a build call.
  • Prototype creates a new object by copying an existing, pre-configured instance.
  • Factory and Abstract Factory answer which type to create; Builder and Prototype answer how to assemble or copy.
  • In modern Java, Factory Method often becomes a Map of Supplier, and Prototype uses a copy constructor.

Definition

Factory Method picks one product, Abstract Factory picks a matching family, Builder assembles one complex product step by step, and Prototype copies an existing product.
  • Factory Method – defines a method that decides which concrete class to instantiate; caller asks by key and never writes new.
  • Abstract Factory – produces whole families of related objects that must be used together; swapping the factory swaps the entire family.
  • Builder – constructs a complex object step by step through chained setters; the build() call validates and produces an immutable result.
  • Prototype – creates new objects by cloning an existing instance rather than constructing from scratch.
  • Key distinction – Factory and Abstract Factory answer which type to create; Builder and Prototype answer how a single object is assembled or copied.

At the National Bank of India, Factory Method is the single counter that hands Rajesh the right account-opening kit based on the type he asks for. Abstract Factory is the regional office that hands him a whole matching set: forms, card design, and cheque book for his region. Builder is how he fills a long loan application one field at a time and only submits when it is complete. Prototype is photocopying a standard pre-filled template form and tweaking a couple of fields instead of writing the whole form again.

Understanding the Concepts

What is the Factory Method pattern?

Factory Method centralises the decision of which concrete class to create behind a method. The caller asks for a product by a key and never writes new.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public interface SettlementStrategy {
    void settle(String tradeId, BigDecimal amount);
}

public class EquitySettlement implements SettlementStrategy {
    public void settle(String tradeId, BigDecimal amount) {
        System.out.println("Settling equity trade " + tradeId + ": " + amount);
    }
}

public class FxSettlement implements SettlementStrategy {
    public void settle(String tradeId, BigDecimal amount) {
        System.out.println("Settling FX trade " + tradeId + ": " + amount);
    }
}

public class SettlementFactory {
    public static SettlementStrategy create(String tradeType) {
        return switch (tradeType) {
            case "EQUITY" -> new EquitySettlement();
            case "FX" -> new FxSettlement();
            default -> throw new IllegalArgumentException("Unknown: " + tradeType);
        };
    }
}

Use it when you have one product hierarchy and the choice depends on input or configuration.

What is the Abstract Factory pattern?

Abstract Factory produces families of related products that must be used together. Each concrete factory produces one complete, consistent family.

JAVA
package com.bank.report;

public interface ReportHeader {
    String header();
}

public interface ReportFormatter {
    String format(String content);
}

// Abstract Factory interface
public interface ReportKitFactory {
    ReportHeader createHeader();
    ReportFormatter createFormatter();
}

// Regulatory family
public class RegulatoryHeader implements ReportHeader {
    public String header() { return "[REGULATORY FILING]"; }
}
public class RegulatoryFormatter implements ReportFormatter {
    public String format(String c) { return "FORMAL: " + c; }
}
public class RegulatoryKitFactory implements ReportKitFactory {
    public ReportHeader createHeader() { return new RegulatoryHeader(); }
    public ReportFormatter createFormatter() { return new RegulatoryFormatter(); }
}

// Customer family
public class CustomerHeader implements ReportHeader {
    public String header() { return "Dear Valued Customer,"; }
}
public class CustomerFormatter implements ReportFormatter {
    public String format(String c) { return "Friendly: " + c; }
}
public class CustomerKitFactory implements ReportKitFactory {
    public ReportHeader createHeader() { return new CustomerHeader(); }
    public ReportFormatter createFormatter() { return new CustomerFormatter(); }
}

Swapping the factory swaps the entire matching set.

What is the Builder pattern?

Builder solves the telescoping-constructor problem. Each field gets a named setter that returns the builder itself, and build() validates and produces an immutable object.

JAVA
package com.bank.model;

import java.math.BigDecimal;

public final class TradeOrder {
    private final String tradeId;
    private final String symbol;
    private final int quantity;
    private final BigDecimal price;
    private final BigDecimal fee;

    private TradeOrder(Builder b) {
        this.tradeId = b.tradeId;
        this.symbol = b.symbol;
        this.quantity = b.quantity;
        this.price = b.price;
        this.fee = b.fee;
    }

    public static Builder builder() { return new Builder(); }

    public static class Builder {
        private String tradeId;
        private String symbol;
        private int quantity;
        private BigDecimal price;
        private BigDecimal fee = BigDecimal.ZERO;

        public Builder tradeId(String v) { this.tradeId = v; return this; }
        public Builder symbol(String v) { this.symbol = v; return this; }
        public Builder quantity(int v) { this.quantity = v; return this; }
        public Builder price(BigDecimal v) { this.price = v; return this; }
        public Builder fee(BigDecimal v) { this.fee = v; return this; }

        public TradeOrder build() {
            if (tradeId == null || symbol == null) {
                throw new IllegalStateException("tradeId and symbol are required");
            }
            if (quantity <= 0) {
                throw new IllegalStateException("quantity must be positive");
            }
            return new TradeOrder(this);
        }
    }

    @Override
    public String toString() {
        return tradeId + ": " + quantity + " " + symbol + " @ " + price + " fee=" + fee;
    }
}

What is the Prototype pattern?

Prototype creates a new object by copying an existing one. Use a copy constructor rather than Cloneable.

JAVA
package com.bank.model;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

public class OrderTemplate {
    private final String exchange;
    private final BigDecimal defaultFee;
    private final List<String> tags;

    public OrderTemplate(String exchange, BigDecimal defaultFee, List<String> tags) {
        this.exchange = exchange;
        this.defaultFee = defaultFee;
        this.tags = new ArrayList<>(tags);
    }

    // Copy constructor – deep copy
    public OrderTemplate(OrderTemplate other) {
        this.exchange = other.exchange;
        this.defaultFee = other.defaultFee;
        this.tags = new ArrayList<>(other.tags); // deep copy the mutable list
    }

    public OrderTemplate copy() {
        return new OrderTemplate(this);
    }

    @Override
    public String toString() {
        return exchange + " fee=" + defaultFee + " tags=" + tags;
    }
}

When do you use each pattern?

| Pattern | Question it answers | Use when |
|---|---|---|
| Factory Method | Which one concrete type? | One product hierarchy, choice depends on input |
| Abstract Factory | Which matching family? | Multiple related products must be consistent |
| Builder | How to assemble step by step? | Many optional fields, immutable result |
| Prototype | How to copy an existing object? | Expensive construction, pre-configured templates |

How do these patterns look in modern Java?

Factory Method often becomes a Map>, eliminating the switch entirely:

JAVA
package com.bank.service;

import java.util.Map;
import java.util.function.Supplier;

public class ModernSettlementFactory {
    private static final Map<String, Supplier<SettlementStrategy>> REGISTRY = Map.of(
        "EQUITY", EquitySettlement::new,
        "FX", FxSettlement::new
    );

    public static SettlementStrategy create(String type) {
        Supplier<SettlementStrategy> supplier = REGISTRY.get(type);
        if (supplier == null) throw new IllegalArgumentException("Unknown: " + type);
        return supplier.get();
    }
}

Prototype uses a copy constructor instead of Cloneable. Builder is often generated by Lombok's @Builder.

What are common mistakes with creational patterns?

The most common mistake is using a Singleton where a plain injected dependency would do, adding global state and complicating testing. Another error is choosing Factory Method when a simple constructor is sufficient, adding a layer of indirection for no benefit. A third mistake is using Builder for objects with only two or three required fields, where a constructor is clearer.

JAVA
package com.bank.pattern;

import java.math.BigDecimal;

public class CreationalMistakes {
    // WRONG: Singleton for a stateless service
    // public class TransferValidator {
    //     private static final TransferValidator INSTANCE = new TransferValidator();
    //     public static TransferValidator getInstance() { return INSTANCE; }
    // }

    // CORRECT: Let Spring inject it as a bean
    // @Service public class TransferValidator { ... }

    // WRONG: Builder for a simple 2-field object
    // Transaction t = Transaction.builder().amount(x).type(y).build();

    // CORRECT: Just use the constructor
    // Transaction t = new Transaction(amount, type);

    public static void main(String[] args) {
        System.out.println("WRONG: Singleton for stateless service (use DI instead)");
        System.out.println("WRONG: Factory for one concrete type (use constructor)");
        System.out.println("WRONG: Builder for 2-field object (overkill)");
        System.out.println();
        System.out.println("CORRECT: Use creational patterns when the problem matches:");
        System.out.println("  Factory: multiple variants selected at runtime");
        System.out.println("  Builder: many optional parameters");
        System.out.println("  Prototype: expensive-to-create objects with small variation");
    }
}
TEXT
Expected output:
WRONG: Singleton for stateless service (use DI instead)
WRONG: Factory for one concrete type (use constructor)
WRONG: Builder for 2-field object (overkill)

CORRECT: Use creational patterns when the problem matches:
  Factory: multiple variants selected at runtime
  Builder: many optional parameters
  Prototype: expensive-to-create objects with small variation

What should a senior engineer know?

  • Factory Method and Abstract Factory answer which type to create. Builder and Prototype answer how to assemble or copy.
  • In Spring, the bean container itself is a Factory. Injecting a Map gives a factory for free.
  • Overusing Abstract Factory adds interfaces for no benefit if products do not need to match.
JAVA
package com.bank.demo;

import java.math.BigDecimal;
import java.util.List;

public class CreationalPatternsDemo {
    public static void main(String[] args) {
        // Factory
        SettlementStrategy s = SettlementFactory.create("EQUITY");
        s.settle("TRD-001", new BigDecimal("500000"));

        // Builder
        TradeOrder order = TradeOrder.builder()
            .tradeId("TRD-002").symbol("RELIANCE")
            .quantity(100).price(new BigDecimal("2450"))
            .fee(new BigDecimal("50")).build();
        System.out.println(order);

        // Prototype
        OrderTemplate template = new OrderTemplate("NSE", new BigDecimal("20"), List.of("DEFAULT"));
        OrderTemplate copy = template.copy();
        System.out.println("Template: " + template);
        System.out.println("Copy: " + copy);
    }
}
TEXT
Settling equity trade TRD-001: 500000
TRD-002: 100 RELIANCE @ 2450 fee=50
Template: NSE fee=20 tags=[DEFAULT]
Copy: NSE fee=20 tags=[DEFAULT]

Visual Overview

DIAGRAM
  Creational Patterns – What Each Solves
  ───────────────────────────────────────

  Factory Method:
  Client ──▶ Factory.create("EQUITY") ──▶ EquitySettlement
             (which one type?)

  Abstract Factory:
  Client ──▶ RegulatoryKitFactory ──▶ RegulatoryHeader
                                  ──▶ RegulatoryFormatter
             (which matching family?)

  Builder:
  Client ──▶ builder().tradeId().symbol().quantity().build()
             (step-by-step assembly → immutable result)

  Prototype:
  Template ──▶ template.copy() ──▶ independent deep copy
              (copy existing, tweak a few fields)

Key Takeaway

Factory Method, Abstract Factory, Builder, and Prototype are four creational patterns, each solving a different construction problem.

Factory Method defines a single method that decides which concrete class to instantiate. Rajesh uses it at National Bank of India when picking an equity or FX settlement handler based on the trade type. It supports OCP because adding a new type means adding a class and one branch.

Abstract Factory goes a level higher and produces whole families of related objects. Pankaj uses it when generating documents: a regulatory filing needs a matching regulatory header and formatter, while a customer statement needs a friendly set. One factory hands back the entire matching family, and swapping the factory swaps everything.

Builder solves the telescoping-constructor problem. When a trade order has many fields, some optional, the Builder lets Rajesh set fields by name through chained calls and call build() to produce an immutable, validated object. Prototype creates a new object by cloning an existing one, which Rajesh uses for pre-configured order templates: copy the NSE template and tweak the symbol and quantity.

The senior point is that Factory and Abstract Factory answer which type to create, while Builder and Prototype answer how a single object is assembled or copied. In modern Java, Factory Method frequently becomes a Map, and Prototype uses a copy constructor rather than Cloneable.

Remember one sentence: Factory picks the type, Abstract Factory picks a family, Builder assembles step by step, and Prototype copies an existing template.

---

Share
Use ← → arrow keys to navigate