Factory Method Pattern
The Factory Method is that defines an interface for creating an object, but lets subclasses decide which concrete class to instantiate.
Instead of using new directly, object creation is delegated to a factory method.
This promotes loose coupling, follows the Open/Closed Principle, and makes the code easier to extend without modifying existing logic.
Scenario
You’re building a system that supports multiple payment methods:
- Credit Card
- PayPal
- Crypto
Instead of writing:
if (type.equals("card")) new CreditCardPayment();
When we add more CashPayment, we need to modify logic code. If we use Factory Method, we don't edit existing logic code, just add new
We use Factory Method to delegate object creation to subclasses.
1. Product Interface
interface Payment {
void processPayment(double amount);
}
2. Concrete Products
class CreditCardPayment implements Payment {
public void processPayment(double amount) {
System.out.println("Processing credit card payment: $" + amount);
}
}
class PayPalPayment implements Payment {
public void processPayment(double amount) {
System.out.println("Processing PayPal payment: $" + amount);
}
}
class CryptoPayment implements Payment {
public void processPayment(double amount) {
System.out.println("Processing crypto payment: $" + amount);
}
}
3. Creator (Abstract Factory Class)
This contains the Factory Method.
abstract class PaymentFactory {
// Factory Method
public abstract Payment createPayment();
// Business logic using the product
public void executePayment(double amount) {
Payment payment = createPayment(); // <-- delegated
payment.processPayment(amount);
}
}
4. Concrete Factories
class CreditCardFactory extends PaymentFactory {
public Payment createPayment() {
return new CreditCardPayment();
}
}
class PayPalFactory extends PaymentFactory {
public Payment createPayment() {
return new PayPalPayment();
}
}
class CryptoFactory extends PaymentFactory {
public Payment createPayment() {
return new CryptoPayment();
}
}
5. Client Code
public class Main {
public static void main(String[] args) {
PaymentFactory creditCardFactory = new CreditCardFactory ();
creditCardFactory.executePayment(100.0);
PaymentFactory payPalFactory = new PayPalFactory();
payPalFactory.executePayment(100.0);
}
}
We shouldn’t use Factory Method when object creation is simple, doesn’t vary by subclass, or when a static/simple factory is enough. Using it unnecessarily increases complexity and violates the YAGNI principle. It’s best used when we expect extensibility and want to decouple creation from business logic.
Simple Factory
Cleaner for small systems.
public class PaymentFactory {
public static Payment create(String type) {
switch(type) {
case "paypal": return new PayPalPayment();
case "card": return new CreditCardPayment();
default: throw new IllegalArgumentException();
}
}
}
Spring Framework
Spring Framework often make the Factory Method Pattern unnecessary because the IoC container already acts like a factory.
Payment payment = new PayPalPayment();
Spring creates the object and injects it for you.
@Service
public class PaymentService {
private final Payment payment;
public PaymentService(Payment payment) {
this.payment = payment;
}
public void process(double amount) {
payment.processPayment(amount);
}
}
Implementation:
@Component
public class PayPalPayment implements Payment {
public void processPayment(double amount) {
System.out.println("PayPal payment: " + amount);
}
}
Example Using Spring Bean Selection
If multiple implementations exist:
@Component
public class CreditCardPayment implements Payment {}
@Component
public class PayPalPayment implements Payment {}
You can choose with:
@Qualifier("payPalPayment")
private Payment payment;