Skip to content

✅ What is Factory Design Pattern?

🔹 Definition

Factory Pattern is a creational design pattern used to:

Create objects without exposing the object creation logic to the client.

👉 Instead of using new, the client calls a factory method.

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.


🎯 Best Way to Explain in Interview

"Factory pattern provides a way to create objects without exposing instantiation logic, promoting loose coupling. The client interacts with a factory instead of directly creating objects using constructors.

✅ Why Use Factory Pattern?

  • Encapsulates object creation logic
  • Promotes loose coupling
  • Makes code scalable & maintainable
  • Easy to add new types without changing client code

✅ Real-life Example : Payment Gateway

This is a realistic example where an e-commerce application supports multiple payment methods:

  • UPI
  • Credit Card
  • Stripe
  • Net Banking

The client only asks the factory for a payment method and does not know the concrete implementation.



1. PaymentType.java

public enum PaymentType {
    UPI,
    CREDIT_CARD,
    STRIPE,
    NET_BANKING
}

2. PaymentRequest.java

Marker interface for all payment requests.

Whether PaymentRequest is truly a marker interface depends on its intent: - If it's only used to mark classes → Marker Interface - If it's used as a common type for polymorphism → Base/Tagging Interface (more common in application code)

public interface PaymentRequest {
}

3. Payment request Implementations

UpiPaymentRequest.java

public class UpiPaymentRequest implements PaymentRequest {

    private final String upiId;

    public UpiPaymentRequest(String upiId) {
        this.upiId = upiId;
    }

    public String getUpiId() {
        return upiId;
    }
}

CreditCardPaymentRequest.java

public class CreditCardPaymentRequest
        implements PaymentRequest {

    private final String cardNumber;
    private final String cardHolderName;
    private final String expiryDate;
    private final String cvv;

    public CreditCardPaymentRequest(
            String cardNumber,
            String cardHolderName,
            String expiryDate,
            String cvv) {

        this.cardNumber = cardNumber;
        this.cardHolderName = cardHolderName;
        this.expiryDate = expiryDate;
        this.cvv = cvv;
    }

    public String getCardNumber() {
        return cardNumber;
    }

    public String getCardHolderName() {
        return cardHolderName;
    }

    public String getExpiryDate() {
        return expiryDate;
    }

    public String getCvv() {
        return cvv;
    }
}

StripePaymentRequest.java

public class StripePaymentRequest
        implements PaymentRequest {

    private final String token;

    public StripePaymentRequest(String token) {
        this.token = token;
    }

    public String getToken() {
        return token;
    }
}

NetBankingPaymentRequest.java

public class NetBankingPaymentRequest
        implements PaymentRequest {

    private final String bankName;
    private final String accountNumber;

    public NetBankingPaymentRequest(
            String bankName,
            String accountNumber) {

        this.bankName = bankName;
        this.accountNumber = accountNumber;
    }

    public String getBankName() {
        return bankName;
    }

    public String getAccountNumber() {
        return accountNumber;
    }
}

4. PaymentGateway.java

public interface PaymentGateway {

    void pay(
            double amount,
            PaymentRequest request);
}

5. Conrete implementation

UpiPayment.java

public class UpiPayment
        implements PaymentGateway {

    @Override
    public void pay(
            double amount,
            PaymentRequest request) {

        UpiPaymentRequest upiRequest =
                (UpiPaymentRequest) request;

        System.out.println("\n===== UPI PAYMENT =====");

        System.out.println(
                "UPI ID: "
                        + upiRequest.getUpiId());

        System.out.println(
                "Amount: ₹" + amount);

        System.out.println(
                "Payment Successful");
    }
}

CreditCardPayment.java

public class CreditCardPayment
        implements PaymentGateway {

    @Override
    public void pay(
            double amount,
            PaymentRequest request) {

        CreditCardPaymentRequest cardRequest =
                (CreditCardPaymentRequest) request;

        System.out.println(
                "\n===== CREDIT CARD PAYMENT =====");

        System.out.println(
                "Card Holder: "
                        + cardRequest.getCardHolderName());

        System.out.println(
                "Card Number: "
                        + cardRequest.getCardNumber());

        System.out.println(
                "Expiry: "
                        + cardRequest.getExpiryDate());

        System.out.println(
                "Amount: ₹" + amount);

        System.out.println(
                "Payment Successful");
    }
}

StripePayment.java

public class StripePayment
        implements PaymentGateway {

    @Override
    public void pay(
            double amount,
            PaymentRequest request) {

        StripePaymentRequest stripeRequest =
                (StripePaymentRequest) request;

        System.out.println(
                "\n===== STRIPE PAYMENT =====");

        System.out.println(
                "Stripe Token: "
                        + stripeRequest.getToken());

        System.out.println(
                "Amount: ₹" + amount);

        System.out.println(
                "Calling Stripe API...");

        System.out.println(
                "Payment Successful");
    }
}

NetBankingPayment.java

public class NetBankingPayment
        implements PaymentGateway {

    @Override
    public void pay(
            double amount,
            PaymentRequest request) {

        NetBankingPaymentRequest netBankingRequest =
                (NetBankingPaymentRequest) request;

        System.out.println(
                "\n===== NET BANKING PAYMENT =====");

        System.out.println(
                "Bank: "
                        + netBankingRequest.getBankName());

        System.out.println(
                "Account Number: "
                        + netBankingRequest.getAccountNumber());

        System.out.println(
                "Amount: ₹" + amount);

        System.out.println(
                "Payment Successful");
    }
}

5. PaymentFactory.java

public class PaymentFactory {

    public static PaymentGateway getPaymentGateway(
            PaymentType paymentType) {

        return switch (paymentType) {

            case UPI ->
                    new UpiPayment();

            case CREDIT_CARD ->
                    new CreditCardPayment();

            case STRIPE ->
                    new StripePayment();

            case NET_BANKING ->
                    new NetBankingPayment();
        };
    }
}

6. CheckoutService.java

public class CheckoutService {

    public void checkout(
            double amount,
            PaymentType paymentType,
            PaymentRequest request) {

        PaymentGateway paymentGateway =
                PaymentFactory
                        .getPaymentGateway(paymentType);

        paymentGateway.pay(
                amount,
                request);
    }
}

7. Main.java

public class Main {

    public static void main(String[] args) {

        CheckoutService checkoutService =
                new CheckoutService();

        // UPI Payment
        checkoutService.checkout(
                5000,
                PaymentType.UPI,
                new UpiPaymentRequest(
                        "john@okhdfcbank")
        );

        // Credit Card Payment
        checkoutService.checkout(
                10000,
                PaymentType.CREDIT_CARD,
                new CreditCardPaymentRequest(
                        "4111111111111111",
                        "John Doe",
                        "12/30",
                        "123")
        );

        // Stripe Payment
        checkoutService.checkout(
                15000,
                PaymentType.STRIPE,
                new StripePaymentRequest(
                        "stripe_token_123")
        );

        // Net Banking Payment
        checkoutService.checkout(
                20000,
                PaymentType.NET_BANKING,
                new NetBankingPaymentRequest(
                        "HDFC Bank",
                        "1234567890")
        );
    }
}

Output

===== UPI PAYMENT =====
UPI ID: john@okhdfcbank
Amount: ₹5000.0
Payment Successful

===== CREDIT CARD PAYMENT =====
Card Holder: John Doe
Card Number: 4111111111111111
Expiry: 12/30
Amount: ₹10000.0
Payment Successful

===== STRIPE PAYMENT =====
Stripe Token: stripe_token_123
Amount: ₹15000.0
Calling Stripe API...
Payment Successful

===== NET BANKING PAYMENT =====
Bank: HDFC Bank
Account Number: 1234567890
Amount: ₹20000.0
Payment Successful

Real Production Example

Usually each payment gateway requires different logic.

public class StripePayment implements PaymentGateway {

    @Override
    public void pay(double amount) {

        // Create payment intent
        // Call Stripe API
        // Verify response
        // Update transaction status

        System.out.println(
            "Calling Stripe API for amount ₹" + amount
        );
    }
}
public class UpiPayment implements PaymentGateway {

    @Override
    public void pay(double amount) {

        // Generate UPI QR
        // Create transaction request
        // Poll payment status

        System.out.println(
            "Generating UPI request for ₹" + amount
        );
    }
}

The client never changes:

PaymentGateway gateway =
        PaymentFactory.createPaymentGateway(paymentType);

gateway.pay(amount);

This is the key benefit of the Factory Pattern: new payment methods can be added with minimal changes to client code.


Interview-Level Improvement

Instead of a large switch, use a ma / registry-based factory:

public class PaymentFactory {

    private static final Map<PaymentType,
            Supplier<PaymentGateway>> registry =
            Map.of(
                PaymentType.UPI, UpiPayment::new,
                PaymentType.CREDIT_CARD, CreditCardPayment::new,
                PaymentType.STRIPE, StripePayment::new,
                PaymentType.NET_BANKING, NetBankingPayment::new
            );

    public static PaymentGateway createPaymentGateway(
            PaymentType type) {

        return registry.get(type).get();
    }
}

Benefits

  • No huge switch statement.
  • Easy to register new payment methods.
  • Commonly used in Spring Boot and enterprise applications.
  • Better follows the Open/Closed Principle.

This is closer to how factory-based object creation is implemented in large-scale backend systems.