Factory Design Pattern in Java: Explained Simply
What is the Factory Pattern?
The Factory Pattern is a creational design pattern. It moves object creation out of your business code and into one dedicated place, called the factory.
Real-life analogy: When you order food at a restaurant, you say “I want a pizza” and the kitchen decides how to make it. You don’t go into the kitchen, pick the ingredients, and bake it yourself. The kitchen is the factory.
Get Govinda Ekabote’s stories in your inbox
Join Medium for free to get updates from this writer.
In code, the client says “give me a notification sender of type EMAIL” and the factory returns the right object. The client never writes new EmailNotification().
The Problem: Without a Factory
Say you are building a notification system that supports Email and SMS.
public class OrderService {
public void placeOrder(String type, String message) {
if (type.equals("EMAIL")) {
EmailNotification email = new EmailNotification();
email.send(message);
} else if (type.equals("SMS")) {
SmsNotification sms = new SmsNotification();
sms.send(message);
}
// Tomorrow: Push, WhatsApp, Slack... this keeps growing
}
}What’s wrong here?
OrderServiceis tightly coupled to the concrete classes.- Every new notification type means editing this class, which breaks the Open/Closed Principle.
- The same
if-elseends up copy-pasted in many places. - It is hard to unit test.
The Solution: Factory Pattern
Step 1: Create a common interface
public interface Notification {
void send(String message);
}Step 2: Create concrete implementations
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending EMAIL: " + message);
}
}
public class SmsNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}
public class PushNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending PUSH: " + message);
}
}Step 3: Create the Factory
public class NotificationFactory {
public static Notification createNotification(String type) {
if (type == null || type.isBlank()) {
throw new IllegalArgumentException("Notification type cannot be empty");
}
return switch (type.toUpperCase()) {
case "EMAIL" -> new EmailNotification();
case "SMS" -> new SmsNotification();
case "PUSH" -> new PushNotification();
default -> throw new IllegalArgumentException("Unknown type: " + type);
};
}
}Step 4: Use it in the client code
public class OrderService {
public void placeOrder(String type, String message) {
Notification notification = NotificationFactory.createNotification(type);
notification.send(message);
}
}Step 5: Run it
public class Main {
public static void main(String[] args) {
OrderService service = new OrderService();
service.placeOrder("EMAIL", "Your order is placed!");
service.placeOrder("SMS", "Your order is shipped!");
service.placeOrder("PUSH", "Your order is delivered!");
}
}Sending EMAIL: Your order is placed!
Sending SMS: Your order is shipped!
Sending PUSH: Your order is delivered!OrderService now only knows about the Notification interface. It doesn’t know or care which class does the work.
When to Use It
Use the Factory Pattern when:
- You have multiple implementations of the same interface (payments, notifications, report exporters, parsers).
- The exact type is decided at runtime, from user input, config, or a request parameter.
- You want to hide complex object creation from the client.
Skip it when you only have one implementation and don’t expect more. Don’t over-engineer.
Common Interview Question
Q: What is the difference between Factory and Abstract Factory?
- Factory: creates one type of product (for example, a
Notification). - Abstract Factory: creates families of related products (for example, a
WindowsUIFactorycreating Windows Button + Windows Checkbox, and aMacUIFactorycreating Mac Button + Mac Checkbox).
Conclusion
The Factory Pattern comes down to one idea: don’t create objects directly in your business logic. Ask a factory to do it. That keeps your code flexible, clean, and easy to extend. You already use it every day in Spring through BeanFactory and ApplicationContext.










