Factory Method Pattern
Define an interface for creating an object, but let subclasses — or a mapping — decide which class to instantiate.
Intent
Factory Method moves the new keyword behind a method so that callers depend on an abstraction rather than a concrete class. The creator declares a method that returns a product interface; subclasses (or a lookup table) decide which concrete product to build.
Problem
Notification n;
if (channel.equals("EMAIL")) n = new EmailNotification();
else if (channel.equals("SMS")) n = new SmsNotification();
else if (channel.equals("PUSH")) n = new PushNotification();
This if chain gets copied into every place that needs a notification. Adding a channel means hunting down every copy.
Structure
classDiagram
class Creator {
<<abstract>>
+send(msg) void
#createNotification()* Notification
}
class EmailCreator {
#createNotification() Notification
}
class SmsCreator {
#createNotification() Notification
}
class Notification {
<<interface>>
+deliver(msg) void
}
Creator <|-- EmailCreator
Creator <|-- SmsCreator
Notification <|.. EmailNotification
Notification <|.. SmsNotification
Creator ..> Notification : creates
Classic GoF version
The base class contains the workflow and calls an abstract factory method; subclasses override it.
abstract class NotificationSender {
// the template that uses the product
public void send(String message) {
Notification n = createNotification(); // factory method
n.deliver(message);
}
protected abstract Notification createNotification();
}
class EmailSender extends NotificationSender {
@Override protected Notification createNotification() { return new EmailNotification(); }
}
Modern Java version
Most Java code today uses a static factory or a registry of suppliers instead of subclassing. Here is a runnable version combining a sealed interface, records and a switch expression:
import java.util.*;
import java.util.function.Supplier;
sealed interface Notification permits Email, Sms, Push {
void deliver(String to, String message);
}
record Email() implements Notification {
public void deliver(String to, String msg) { System.out.println("📧 EMAIL to " + to + ": " + msg); }
}
record Sms() implements Notification {
public void deliver(String to, String msg) { System.out.println("📱 SMS to " + to + ": " + msg); }
}
record Push() implements Notification {
public void deliver(String to, String msg) { System.out.println("🔔 PUSH to " + to + ": " + msg); }
}
enum Channel { EMAIL, SMS, PUSH }
final class Notifications {
private Notifications() {}
// Static factory method — callers never see the concrete classes
static Notification of(Channel channel) {
return switch (channel) {
case EMAIL -> new Email();
case SMS -> new Sms();
case PUSH -> new Push();
};
}
// Registry variant: new channels can be registered at runtime (e.g. plugins)
private static final Map<String, Supplier<Notification>> REGISTRY = new HashMap<>(Map.of(
"email", Email::new,
"sms", Sms::new));
static Notification byName(String name) {
var supplier = REGISTRY.get(name.toLowerCase());
if (supplier == null) throw new IllegalArgumentException("Unknown channel: " + name);
return supplier.get();
}
}
class Main {
public static void main(String[] args) {
for (Channel c : Channel.values()) {
Notifications.of(c).deliver("ada@example.com", "Your build passed");
}
Notifications.byName("SMS").deliver("+44 7700 900000", "Code: 123456");
}
}Where you’ll see it
List.of(),Optional.of(),Path.of(),LocalDate.of()— static factory methodsCalendar.getInstance(),NumberFormat.getInstance(locale)— return a subclass chosen at runtimeExecutors.newFixedThreadPool(n)— hidesThreadPoolExecutor’s constructor- Spring’s
FactoryBean<T>and@Beanmethods — factory methods managed by the container
Pros and cons
| Pros | Cons |
|---|---|
| Callers depend on an interface, not concrete classes | Classic form needs a creator subclass per product |
| Creation logic lives in one place | Extra indirection for simple cases |
| Can return cached instances or subtypes | Static factories can’t be overridden in tests |
Mark it complete to track your progress through Design Patterns.