Back to Creational: Singleton, Factory, Builder, Prototype
Q11Question 11 of 12

Design using the Factory Method pattern

Key Concepts
  • Factory Method centralises object creation so callers ask for a type and receive the right concrete instance without naming the concrete class.
  • The structure has a product interface, concrete products, and a factory with a creation method that takes a type parameter and returns the product.
  • Callers depend only on the product interface, satisfying Dependency Inversion and Open/Closed principles.
  • Adding a new product type means writing one new class and one case in the factory with zero changes to existing callers.
  • The simple parameterised factory is the most common interview and production form, distinct from the formal Factory Method and Abstract Factory.
  • Creation logic, dependency wiring, and configuration reading are hidden inside the factory instead of duplicated at every call site.

Definition

The Factory Method centralises object creation so callers ask for a type and receive the right concrete instance without naming the concrete class.
  • Factory Method – a creational pattern that defines an interface for creating an object but lets a dedicated factory decide which concrete class to instantiate based on input.
  • Product interface – the common type returned by the factory, for example PaymentMethod, so callers depend only on this abstraction.
  • Concrete products – specific implementations like UpiPayment, CardPayment, and NetBankingPayment that the factory selects at runtime.
  • Parameterised factory – the most common form, where a static create(String type) method uses a type code to pick the right product.
  • Decoupling – callers never use new on concrete classes, so adding a new product requires zero changes to existing caller code.

Think of the account-opening desk at National Bank of India. Rajesh does not personally construct a savings account object; he tells the desk "savings" and the desk produces the right kind of account. If Priya asks for a current account, the same desk builds that instead. The caller names what they want; the factory knows how to construct it. The desk is the factory, the request type is the parameter, and the account handed back is the product.

Understanding the Concepts

What is the Factory Method pattern?

The Factory Method pattern defines an interface for creating an object but lets a dedicated factory decide which concrete class to instantiate based on input. The caller asks for a product by type and receives a ready-to-use object without knowing its concrete class.

In banking terms, when Rajesh needs to process a payment, he does not construct a UpiPayment or a CardPayment directly. He calls the factory with a type code and gets back a PaymentMethod that is ready to use.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public interface PaymentMethod {
    void pay(String accountId, BigDecimal amount);
    String type();
}

Why does the Factory Method pattern exist?

Without a factory, creation logic is scattered across the codebase. Every place that needs a payment method would have its own if-else or switch deciding which class to instantiate. This violates the Open/Closed Principle because adding a new payment type means editing every call site.

The Factory Method solves this by centralising the decision in one place. It satisfies Dependency Inversion because callers depend on the PaymentMethod interface, not on concrete classes. It satisfies Open/Closed because adding a new type means writing one new class and adding one case in the factory.

JAVA
// Without factory: scattered creation logic (bad)
PaymentMethod pm;
if (type.equals("UPI")) {
    pm = new UpiPayment();
} else if (type.equals("CARD")) {
    pm = new CardPayment();
}
// This code is duplicated everywhere a payment is created

How does the Factory Method work internally?

The factory holds the mapping from type codes to concrete classes. When create is called, it looks up the type, instantiates the matching product, and returns it through the product interface. The caller never sees the concrete class.

JAVA
package com.bank.factory;

import com.bank.service.PaymentMethod;
import com.bank.service.UpiPayment;
import com.bank.service.CardPayment;
import com.bank.service.NetBankingPayment;

public class PaymentFactory {

    public static PaymentMethod create(String type) {
        return switch (type.toUpperCase()) {
            case "UPI"  -> new UpiPayment();
            case "CARD" -> new CardPayment();
            case "NETBANKING" -> new NetBankingPayment();
            default -> throw new IllegalArgumentException(
                "Unknown payment type: " + type);
        };
    }
}

How do you implement concrete products?

Each concrete product implements the PaymentMethod interface and contains its own processing logic. The products are fully independent, so a bug in UPI logic cannot affect card logic.

JAVA
package com.bank.service;

import java.math.BigDecimal;
import java.util.logging.Logger;

public class UpiPayment implements PaymentMethod {
    private static final Logger log = Logger.getLogger(UpiPayment.class.getName());

    @Override
    public void pay(String accountId, BigDecimal amount) {
        log.info("Processing UPI payment of INR " + amount
                 + " for account " + accountId);
        // UPI-specific logic: generate VPA, call NPCI gateway
    }

    @Override
    public String type() { return "UPI"; }
}

public class CardPayment implements PaymentMethod {
    private static final Logger log = Logger.getLogger(CardPayment.class.getName());

    @Override
    public void pay(String accountId, BigDecimal amount) {
        log.info("Processing card payment of INR " + amount
                 + " for account " + accountId);
        // Card-specific logic: tokenise card, call acquirer
    }

    @Override
    public String type() { return "CARD"; }
}

public class NetBankingPayment implements PaymentMethod {
    private static final Logger log = Logger.getLogger(NetBankingPayment.class.getName());

    @Override
    public void pay(String accountId, BigDecimal amount) {
        log.info("Processing net-banking payment of INR " + amount
                 + " for account " + accountId);
        // Net-banking-specific logic: redirect to bank portal
    }

    @Override
    public String type() { return "NETBANKING"; }
}

How is Factory Method different from Abstract Factory?

Factory Method creates one product at a time based on a type code. Abstract Factory creates families of related products without specifying their concrete classes.

| Aspect | Factory Method | Abstract Factory |
|---|---|---|
| Creates | One product at a time | Family of related products |
| Input | Type code or parameter | Factory object selection |
| Structure | Single factory class or method | Interface with multiple create methods |
| Use case | Pick one payment method | Create a matched set (UI theme: button + textbox + dialog) |
| Complexity | Low | Higher |

JAVA
// Factory Method: one product
PaymentMethod pm = PaymentFactory.create("UPI");

// Abstract Factory: family of related products
BankingUIFactory factory = new DarkThemeFactory();
Button btn = factory.createButton();
TextField field = factory.createTextField();

What are common mistakes with the Factory Method?

The most common mistake is returning a concrete type instead of the interface, which defeats the purpose of the pattern.

JAVA
// Wrong: caller depends on the concrete class
public static UpiPayment create(String type) {
    return new UpiPayment(); // caller is coupled to UpiPayment
}

// Correct: caller depends only on the interface
public static PaymentMethod create(String type) {
    return new UpiPayment(); // caller sees PaymentMethod
}

Another mistake is forgetting to handle unknown types, which causes silent failures in production.

JAVA
// Wrong: returns null for unknown type
case default -> null; // caller gets NullPointerException later

// Correct: throws immediately with a clear message
case default -> throw new IllegalArgumentException(
    "Unknown payment type: " + type);

How do you use the Factory Method with Spring?

In Spring, the cleanest approach is injecting a Map where Spring populates the map from all beans implementing the interface. The map key is the bean name.

JAVA
package com.bank.service;

import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.Map;

@Component
public class PaymentService {

    private final Map<String, PaymentMethod> strategies;

    public PaymentService(Map<String, PaymentMethod> strategies) {
        this.strategies = strategies;
    }

    public void processPayment(String type, String accountId,
                               BigDecimal amount) {
        PaymentMethod method = strategies.get(type.toUpperCase());
        if (method == null) {
            throw new IllegalArgumentException(
                "No payment method registered for: " + type);
        }
        method.pay(accountId, amount);
    }
}

Adding a new payment type is just writing a new @Component class. Spring adds it to the map automatically with zero code changes to PaymentService.

When should you use the Factory Method?

Use it when the caller should not know or care which concrete class it gets, when creation logic involves configuration or dependency wiring that should be hidden, or when you expect new product types to be added over time.

Do not use it when there is only one possible product and there is no prospect of more, because the factory adds indirection for no benefit.

JAVA
package com.bank.demo;

import com.bank.factory.PaymentFactory;
import com.bank.service.PaymentMethod;
import java.math.BigDecimal;

public class PaymentDemo {
    public static void main(String[] args) {
        PaymentMethod upi = PaymentFactory.create("UPI");
        upi.pay("SAV2001", new BigDecimal("5000.00"));

        PaymentMethod card = PaymentFactory.create("CARD");
        card.pay("SAV2001", new BigDecimal("12500.00"));
    }
}
TEXT
Processing UPI payment of INR 5000.00 for account SAV2001
Processing card payment of INR 12500.00 for account SAV2001

What should a senior engineer know?

The senior distinction is between three related patterns. The simple parameterised factory is a single class with a static method that takes a type code, which is what most interviewers mean and what production code uses for payment methods. The formal Factory Method is a design where subclasses override a creation method, so each subclass produces a different product. The Abstract Factory produces whole families of related objects.

In production, the factory is also the right place to wire dependencies, read configuration, and apply caching. For example, if constructing a CardPayment requires an API key from configuration, that complexity is hidden inside the factory rather than exposed to every caller.

JAVA
// Formal Factory Method: subclasses override creation
public abstract class TransactionProcessor {
    public void process(BigDecimal amount) {
        PaymentMethod pm = createPaymentMethod(); // subclass decides
        pm.pay("SAV2001", amount);
    }
    protected abstract PaymentMethod createPaymentMethod();
}

public class UpiProcessor extends TransactionProcessor {
    @Override
    protected PaymentMethod createPaymentMethod() {
        return new UpiPayment();
    }
}

What are common interview follow-up questions?

  • How does the Factory Method satisfy the Open/Closed Principle? Adding a new product is a new class plus one factory case, with no changes to callers.
  • Can you combine Factory Method with Strategy? Yes, the factory selects the strategy at runtime.
  • How do you handle unknown types? Always throw a clear exception rather than returning null.
  • What is the difference between a factory and a constructor? A factory can return any subtype, cache instances, and hide creation complexity, while a constructor always creates a new instance of exactly one class.

Visual Overview

DIAGRAM
  Client Code
       │
       │  PaymentFactory.create("UPI")
       ▼
  ┌─────────────────────────────────────┐
  │          PaymentFactory              │
  │                                      │
  │  "UPI"  ──→  new UpiPayment()        │
  │  "CARD" ──→  new CardPayment()       │
  │  "NET"  ──→  new NetBankingPayment() │
  └─────────────┬───────────────────────┘
                │
                │  returns PaymentMethod
                ▼
  ┌─────────────────────────────────────┐
  │     <<interface>> PaymentMethod      │
  │  + pay(accountId, amount)            │
  │  + type()                            │
  └─────────────────────────────────────┘
         ▲            ▲            ▲
         │            │            │
   UpiPayment   CardPayment  NetBankingPayment

Key Takeaway

The Factory Method pattern defines a single place responsible for creating objects, so that callers ask for a type and receive the right concrete instance without ever naming the concrete class themselves. At National Bank of India, Rajesh uses it for payment methods and account types, where the calling code should say UPI or savings and not care how that object is wired together.

The structure is a product interface, several concrete products that implement it, and a factory whose creation method takes a type and returns the product. Because callers depend only on the interface, the pattern satisfies Dependency Inversion. Because adding a new payment method is just a new product class plus one case in the factory, it satisfies Open/Closed. The calling code that processes payments never changes.

The real-world benefit is that creation logic lives in exactly one place. When building a payment method involves reading configuration, wiring dependencies, or applying caching, that complexity is hidden inside the factory instead of being copy-pasted at every call site. In Spring, the factory becomes a Map injected automatically, so adding a new provider is writing one class and nothing else.

The senior distinction to draw is between the simple parameterised factory, the formal Factory Method where subclasses override a creation step, and the Abstract Factory that produces whole families of related objects. At National Bank of India, centralising payment-method creation behind a factory is what lets the team onboard a new payment provider by adding one class, with zero churn in the checkout code that has already been tested and approved.

Remember one sentence: the Factory Method centralises object creation so callers ask for a type and the factory delivers the right product, keeping callers decoupled from concrete classes.

---

Share
Use ← → arrow keys to navigate