Factory Method Pattern

The Factory Method Pattern 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. It promotes loose coupling by deferring object instantiation to subclasses, making it ideal for scenarios where the exact type of object needed depends on context.

Important Features

  • Abstract Creator: Defines an interface or abstract class with a factory method for creating objects.
  • Concrete Creators: Subclasses implement the factory method to produce specific objects.
  • Product Interface: Defines the interface for objects the factory method creates.
  • Concrete Products: Specific implementations of the product interface.
  • Deferred Instantiation: Subclasses decide which class to instantiate.

Sample Implementation

// Product interface
interface Product {
    void doStuff();
}

// Concrete products
class ConcreteProductA implements Product {
    public void doStuff() {
        System.out.println("Product A doing stuff");
    }
}

class ConcreteProductB implements Product {
    public void doStuff() {
        System.out.println("Product B doing stuff");
    }
}

// Creator abstract class
abstract class Creator {
    // Factory method
    abstract Product createProduct();
    
    // Common method using the product
    void someOperation() {
        Product product = createProduct();
        product.doStuff();
    }
}

// Concrete creators
class ConcreteCreatorA extends Creator {
    @Override
    Product createProduct() {
        return new ConcreteProductA();
    }
}

class ConcreteCreatorB extends Creator {
    @Override
    Product createProduct() {
        return new ConcreteProductB();
    }
}

// Usage
public class Main {
    public static void main(String[] args) {
        Creator creatorA = new ConcreteCreatorA();
        creatorA.someOperation(); // Output: Product A doing stuff

        Creator creatorB = new ConcreteCreatorB();
        creatorB.someOperation(); // Output: Product B doing stuff
    }
}Code language: PHP (php)

Use case and Implementation

Creating different types of `Account` objects (e.g., savings, checking, loan) based on user input without specifying the exact class.

In the context of creating different types of Account objects based on user input, let’s break down how the Factory Method Pattern can be applied:

  1. Problem Statement: You need to create various types of accounts (SavingsAccount, CheckingAccount, LoanAccount, etc.) based on user input, without the client code specifying the exact class of account.
  2. Solution: Implement a factory method in a superclass (AccountFactory) that defines an interface for creating accounts. Subclasses (SavingsAccountFactory, CheckingAccountFactory, etc.) will then implement this method to create specific types of accounts.
//FactoryMethodDemo.java
// Account interface
interface Account {
    void deposit(double amount);
    void withdraw(double amount);
    double getBalance();
}
// Concrete Savings Account class
class SavingsAccount implements Account {
    private double balance;
    @Override
    public void deposit(double amount) {
        balance += amount;
    }
    @Override
    public void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
        } else {
            System.out.println("Insufficient balance.");
        }
    }
    @Override
    public double getBalance() {
        return balance;
    }
}
// Concrete Checking Account class
class CheckingAccount implements Account {
    private double balance;
    @Override
    public void deposit(double amount) {
        balance += amount;
    }
    @Override
    public void withdraw(double amount) {
        if (balance >= amount) {
            balance -= amount;
        } else {
            System.out.println("Insufficient balance.");
        }
    }
    @Override
    public double getBalance() {
        return balance;
    }
}
// Concrete LoanAccount class
class LoanAccount implements Account {
    private double balance;
    @Override
    public void deposit(double amount) {
        balance += amount;
    }
    @Override
    public void withdraw(double amount) {
        // Loans typically don't allow withdrawals once disbursed
        System.out.println("Cannot withdraw from a loan account.");
    }
    @Override
    public double getBalance() {
        return balance;
    }
}
// AccountFactory interface
interface AccountFactory {
    Account createAccount();
}
// Concrete Savings Account Factory
class SavingsAccountFactory implements AccountFactory {
    @Override
    public Account createAccount() {
        return new SavingsAccount();
    }
}
// Concrete CheckingAccount Factory
class CheckingAccountFactory implements AccountFactory {
    @Override
    public Account createAccount() {
        return new CheckingAccount();
    }
}
// Concrete LoanAccount Factory
class LoanAccountFactory implements AccountFactory {
    @Override
    public Account createAccount() {
        return new LoanAccount();
    }
}
class FactoryMethodDemo {
    public static void main(String[] args) {
        // Assume user input determines which type of account to create
        String accountType = "savings"; // This can be input from the user
        AccountFactory factory;
        // Determine which factory to use based on user input
        if (accountType.equalsIgnoreCase("savings")) {
            factory = new SavingsAccountFactory();
        } else if (accountType.equalsIgnoreCase("checking")) {
            factory = new CheckingAccountFactory();
        } else if (accountType.equalsIgnoreCase("loan")) {
            factory = new LoanAccountFactory();
        } else {
            throw new IllegalArgumentException("Unknown account type");
        }
        // Create the account using the factory
        Account account = factory.createAccount();
        // Perform operations on the account
        account.deposit(1000);
        account.withdraw(500);
        System.out.println("Current balance: " + account.getBalance());
    }
}

/*
D:\>javac FactoryMethodDemo.java

D:\>java FactoryMethodDemo
Current balance: 500.0
*/

Pros

  • Flexibility: Subclasses can define their own object types, promoting extensibility.
  • Loose Coupling: Clients depend on abstractions, not concrete classes.
  • Single Responsibility: Separates object creation from usage logic.

Cons

  • Complexity: Introduces additional classes and interfaces, increasing code complexity.
  • Subclass Proliferation: May lead to many subclasses if there are many product variants.

When to Use

  • When a class cannot anticipate the type of objects it needs to create.
  • When you want to delegate object creation to subclasses.
  • When you need to localize object creation logic (e.g., creating platform-specific UI components).

The Factory Method Pattern is instrumental when you need to instantiate objects of several related classes based on varying conditions, ensuring that the client remains agnostic to the specific implementation details of those objects.

Scroll to Top