Define an interface for creating objects, but let subclasses decide which class to instantiate.
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.
Instead of calling a constructor directly, you call a factory method which returns the object. This gives subclasses the power to decide which concrete class to instantiate.
Key Idea: Define a method for creating objects, but let subclasses choose the actual type. The parent class doesn't know the exact class of the object it creates.
Without Factory Method, your code is tightly coupled to specific classes. Adding new types requires modifying existing code:
// Problem: Hard-coded object creation
public class PaymentService {
public void processPayment(String type, double amount) {
if (type.equals("stripe")) {
StripeGateway gateway = new StripeGateway();
gateway.process(amount);
} else if (type.equals("paypal")) {
PayPalGateway gateway = new PayPalGateway();
gateway.process(amount);
}
// Adding new gateway? Must modify this method!
}
}Declares the interface for objects the factory method creates
Implement the Product interface (StripeGateway, PayPalGateway)
Declares the factory method that returns Product objects
Override factory method to return specific products
// Product Interface
interface PaymentGateway {
void processPayment(double amount);
boolean validateCredentials();
}
// Concrete Products
class StripePaymentGateway implements PaymentGateway {
@Override
public void processPayment(double amount) {
System.out.println("Processing $" + amount + " through Stripe");
}
@Override
public boolean validateCredentials() {
System.out.println("Validating Stripe API keys");
return true;
}
}
class PayPalPaymentGateway implements PaymentGateway {
@Override
public void processPayment(double amount) {
System.out.println("Processing $" + amount + " through PayPal");
}
@Override
public boolean validateCredentials() {
System.out.println("Validating PayPal credentials");
return true;
}
}
class RazorpayPaymentGateway implements PaymentGateway {
@Override
public void processPayment(double amount) {
System.out.println("Processing ₹" + amount + " through Razorpay");
}
@Override
public boolean validateCredentials() {
System.out.println("Validating Razorpay keys");
return true;
}
}
// Creator Abstract Class
abstract class PaymentProcessor {
// Factory Method (to be implemented by subclasses)
protected abstract PaymentGateway createPaymentGateway();
// Template method using factory method
public void executePayment(double amount) {
PaymentGateway gateway = createPaymentGateway();
if (gateway.validateCredentials()) {
gateway.processPayment(amount);
System.out.println("Payment successful!");
} else {
System.out.println("Payment failed: Invalid credentials");
}
}
}
// Concrete Creators
class StripePaymentProcessor extends PaymentProcessor {
@Override
protected PaymentGateway createPaymentGateway() {
return new StripePaymentGateway();
}
}
class PayPalPaymentProcessor extends PaymentProcessor {
@Override
protected PaymentGateway createPaymentGateway() {
return new PayPalPaymentGateway();
}
}
class RazorpayPaymentProcessor extends PaymentProcessor {
@Override
protected PaymentGateway createPaymentGateway() {
return new RazorpayPaymentGateway();
}
}
// Usage Example
public class ECommerceApp {
public static void main(String[] args) {
// Client code works with processors through abstract interface
PaymentProcessor processor;
String paymentMethod = "stripe"; // Could come from user selection
// Choose processor based on configuration
if (paymentMethod.equals("stripe")) {
processor = new StripePaymentProcessor();
} else if (paymentMethod.equals("paypal")) {
processor = new PayPalPaymentProcessor();
} else {
processor = new RazorpayPaymentProcessor();
}
// Process payment (subclass handles gateway creation)
processor.executePayment(100.00);
}
}Switch between Stripe, PayPal, Razorpay without changing business logic
E-commerce checkout systemsSend notifications via Email, SMS, Push, or Slack
Alert and messaging platformsConnect to MySQL, PostgreSQL, MongoDB based on configuration
ORM libraries, data access layersGenerate PDF, Excel, CSV reports with same interface
Reporting and export systemsCreate platform-specific UI components (Web, iOS, Android)
Cross-platform application developmentIterator interface with iterator() factory method lets each collection return its specific iterator implementation
Factory method that creates React elements based on type - can be DOM elements, components, or fragments
Database backends use factory methods to create database-specific connections and query builders