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:
- 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.
- 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.