Factory Method Design Pattern
1. Introduction
Good morning, everyone.
Today we are going to understand the Factory Method Design Pattern.
Before understanding Factory Method, remember one important point:
Creating an object is not always as simple as writing the
newkeyword.
In a small program, we can directly create an object:
EmailNotification notification =
new EmailNotification();
But in a large application:
We may have different types of objects.
Object creation may require configuration.
The required object may be decided at runtime.
We may want to add new object types in the future.
Factory Method helps us manage this object-creation problem.
2. What is Coupling?
Coupling tells us how strongly two classes depend on each other.
Consider this code:
class OrderService {
public void placeOrder() {
System.out.println("Order placed");
EmailNotification notification =
new EmailNotification();
notification.send("Your order is confirmed");
}
}
Here, OrderService directly creates an EmailNotification.
Therefore:
OrderService depends on EmailNotification
This dependency between two classes is called coupling.
3. What is Tight Coupling?
Tight coupling means that one class strongly depends on a particular concrete class.
In our example:
EmailNotification notification =
new EmailNotification();
OrderService knows:
The name of the concrete class
How its object is created
Which constructor it has
Which method it provides
Suppose the requirement changes from Email to SMS.
We will have to modify OrderService:
SMSNotification notification =
new SMSNotification();
This is a problem because the order-placement logic should not change just because the notification type has changed.
Simple definition
Tight coupling means a change in one class can force us to change other classes.
4. What is Loose Coupling?
Loose coupling means that a class depends on an interface or abstraction instead of depending directly on a concrete class.
First, we create a common interface:
interface Notification {
void send(String message);
}
Different notification classes can implement it:
class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Email sent: " + message);
}
}
class SMSNotification implements Notification {
@Override
public void send(String message) {
System.out.println("SMS sent: " + message);
}
}
Now our business code can work with the interface:
Notification notification;
This reference can store different objects:
notification = new EmailNotification();
notification = new SMSNotification();
The business code does not need to know the internal working of Email or SMS.
Simple definition
Loose coupling means that classes depend on a common contract and implementations can be replaced with minimum changes.
Loose coupling does not mean that dependencies completely disappear. It means that concrete dependencies are reduced and kept in controlled places.
5. Is the new Keyword Wrong?
No, the new keyword is not wrong.
We must use new somewhere to create an object.
The problem starts when object-creation logic is spread throughout the business code.
Factory Method does not remove the new keyword.
Factory Method moves the
newkeyword to a more appropriate place.
6. The Main Problem
Suppose our application supports three notification types:
Email notification
SMS notification
Push notification
The common notification process is:
Validate the message.
Create a notification object.
Send the message.
Store a log.
The process is the same for all notifications.
Only the object changes:
Email service creates EmailNotification
SMS service creates SMSNotification
Push service creates PushNotification
We want the common process to remain in a parent class, but we want child classes to decide which object should be created.
This is where we use the Factory Method Pattern.
7. Factory Method Definition
Factory Method is a creational design pattern in which a parent Creator class defines a method for creating an object, but its child classes decide which concrete object should be created.
In simple words:
The parent says, “Create an object.” The child says, “I will decide which object to create.”
8. Structure of Factory Method
Factory Method normally contains the following parts:
| Role | Responsibility |
|---|---|
| Product | Defines the common behaviour |
| Concrete Product | Provides the actual implementation |
| Creator | Declares the Factory Method |
| Concrete Creator | Overrides the Factory Method and creates a Product |
| Client | Uses the Creator |
In our example:
| Role | Class |
|---|---|
| Product | Notification |
| Concrete Products | EmailNotification, SMSNotification |
| Creator | NotificationService |
| Factory Method | createNotification() |
| Concrete Creators | EmailNotificationService, SMSNotificationService |
| Client | Main |
9. Step One: Product Interface
interface Notification {
void send(String message);
}
Notification is called the Product interface.
It defines a common contract:
Every notification must provide a
send()method.
Email and SMS may send messages differently, but the client can use both through the same method:
notification.send(message);
10. Step Two: Concrete Products
class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Email sent: " + message);
}
}
class SMSNotification implements Notification {
@Override
public void send(String message) {
System.out.println("SMS sent: " + message);
}
}
class PushNotification implements Notification {
@Override
public void send(String message) {
System.out.println(
"Push notification sent: " + message
);
}
}
These are called Concrete Products because these are the actual objects created by our Factory Methods.
11. Step Three: Creator Class
abstract class NotificationService {
protected abstract Notification createNotification();
public void notifyUser(String message) {
if (message == null || message.isBlank()) {
throw new IllegalArgumentException(
"Message cannot be empty"
);
}
System.out.println("Validating message");
Notification notification =
createNotification();
notification.send(message);
System.out.println("Saving notification log");
}
}
NotificationService is called the Creator class.
It contains two important methods.
Factory Method
protected abstract Notification createNotification();
This is the actual Factory Method.
The parent class knows that it needs a Notification object, but it does not know whether that object will be:
EmailNotificationSMSNotificationPushNotification
The child classes will make that decision.
Business Method
public void notifyUser(String message)
This method contains the common notification workflow:
Validate → Create → Send → Log
The common workflow stays in the parent class because it is the same for every notification.
12. Why is the Factory Method Abstract?
Suppose the parent class creates Email directly:
public Notification createNotification() {
return new EmailNotification();
}
Now the parent class is connected to EmailNotification.
It is no longer general.
Therefore, we make the method abstract:
protected abstract Notification createNotification();
This tells every child class:
You must decide which Notification object should be created.
13. Step Four: Concrete Creators
Email Creator
class EmailNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new EmailNotification();
}
}
This child class decides that an EmailNotification should be created.
SMS Creator
class SMSNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new SMSNotification();
}
}
Push Creator
class PushNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new PushNotification();
}
}
These classes are called Concrete Creators.
Every Concrete Creator creates its corresponding Concrete Product.
14. Client Code
public class Main {
public static void main(String[] args) {
NotificationService service =
new EmailNotificationService();
service.notifyUser(
"Your order has been confirmed"
);
}
}
Output:
Validating message
Email sent: Your order has been confirmed
Saving notification log
For SMS, we only change the Creator:
NotificationService service =
new SMSNotificationService();
service.notifyUser("Your OTP is 4582");
The method call remains the same:
service.notifyUser(message);
15. Execution Flow
Consider this line:
NotificationService service =
new EmailNotificationService();
The reference type is:
NotificationService
The actual object is:
EmailNotificationService
Now we call:
service.notifyUser("Order confirmed");
The following steps happen:
notifyUser()starts message validation.It calls
createNotification().The actual object is
EmailNotificationService.Therefore, the overridden Factory Method of
EmailNotificationServiceexecutes.It creates and returns an
EmailNotification.The parent class stores it inside a
Notificationreference.It calls
notification.send(message).The Email implementation sends the message.
Finally, the log is saved.
16. Where is Polymorphism Used?
Factory Method uses polymorphism at two levels.
Creator polymorphism
NotificationService service =
new EmailNotificationService();
A parent reference stores a child Creator object.
Product polymorphism
Notification notification =
new EmailNotification();
An interface reference stores a Concrete Product object.
Therefore, Factory Method combines:
Abstraction
Inheritance
Method overriding
Runtime polymorphism
17. Where is Loose Coupling?
Look at the parent business workflow:
Notification notification =
createNotification();
notification.send(message);
The parent does not write:
EmailNotification notification =
new EmailNotification();
The parent only knows the Notification interface.
Therefore, the common business workflow is not directly dependent on Email, SMS or Push.
The Concrete Creator still knows its Concrete Product:
return new EmailNotification();
That is acceptable because object creation is the responsibility of the Concrete Creator.
Factory Method does not completely remove concrete dependencies. It isolates them inside small, dedicated Creator classes.
18. Adding WhatsApp Notification
Suppose we want to add WhatsApp notifications.
First, we add a new Product:
class WhatsAppNotification
implements Notification {
@Override
public void send(String message) {
System.out.println(
"WhatsApp message sent: " + message
);
}
}
Then, we add its Creator:
class WhatsAppNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new WhatsAppNotification();
}
}
Client code:
NotificationService service =
new WhatsAppNotificationService();
service.notifyUser("Payment successful");
We did not modify:
NotificationServiceEmailNotificationServiceSMSNotificationServicePushNotificationService
We extended the application by adding new classes.
This gives better support for the Open/Closed Principle:
Software should be open for extension but closed for unnecessary modification.
19. Complete Java Program
interface Notification {
void send(String message);
}
class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Email sent: " + message);
}
}
class SMSNotification implements Notification {
@Override
public void send(String message) {
System.out.println("SMS sent: " + message);
}
}
class PushNotification implements Notification {
@Override
public void send(String message) {
System.out.println(
"Push notification sent: " + message
);
}
}
abstract class NotificationService {
protected abstract Notification createNotification();
public void notifyUser(String message) {
if (message == null || message.isBlank()) {
throw new IllegalArgumentException(
"Message cannot be empty"
);
}
System.out.println("Validating message");
Notification notification =
createNotification();
notification.send(message);
System.out.println("Saving notification log");
}
}
class EmailNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new EmailNotification();
}
}
class SMSNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new SMSNotification();
}
}
class PushNotificationService
extends NotificationService {
@Override
protected Notification createNotification() {
return new PushNotification();
}
}
public class Main {
public static void main(String[] args) {
NotificationService emailService =
new EmailNotificationService();
emailService.notifyUser(
"Your order has been confirmed"
);
System.out.println();
NotificationService smsService =
new SMSNotificationService();
smsService.notifyUser(
"Your OTP is 4582"
);
System.out.println();
NotificationService pushService =
new PushNotificationService();
pushService.notifyUser(
"Your order is out for delivery"
);
}
}
20. Simple Factory vs Factory Method
Simple Factory
A single Factory class decides which object to create:
if (type.equals("EMAIL")) {
return new EmailNotification();
}
if (type.equals("SMS")) {
return new SMSNotification();
}
Factory Method
The parent declares a Factory Method:
protected abstract Notification createNotification();
Different child Creators override it:
class EmailNotificationService {
protected Notification createNotification() {
return new EmailNotification();
}
}
| Simple Factory | Factory Method |
|---|---|
| One Factory handles every type | Different Creator subclasses |
Usually uses if-else or switch |
Uses inheritance and overriding |
| Factory decides the Product | Child Creator decides the Product |
| Easier for small applications | Better for extensible workflows |
| Factory changes for a new Product | A new Creator can be added |
21. Advantages
Loose coupling
The common workflow depends on the Product interface, not on individual Concrete Products.
Reusable workflow
Validation, sending and logging logic remains in one parent Creator class.
Easy extension
New Product and Creator classes can be added without changing the existing workflow.
Controlled object creation
The responsibility of creating objects remains inside Creator classes.
Better maintainability
A change in one Product’s creation logic is normally handled by its corresponding Creator.
22. Disadvantages
More classes
Every new Product may require a new Concrete Creator.
More complex structure
Students must understand two hierarchies:
Product hierarchy
Creator hierarchy
Overengineering for simple problems
If the application has only one simple object type, Factory Method may add unnecessary complexity.
23. When Should We Use Factory Method?
Use Factory Method when:
Multiple implementations share a common interface.
A common workflow needs different objects.
Child classes should decide which object to create.
The exact object type may change or grow in the future.
We want to keep object-creation details away from common business logic.
Examples include:
Email, SMS and Push notifications
Road, Sea and Air logistics
PDF, Word and Excel documents
MySQL and PostgreSQL connections
Windows and Web user-interface components
24. When Should We Not Use It?
Avoid Factory Method when:
There is only one simple object type.
Object creation is unlikely to change.
There is no common workflow.
Creating additional Creator subclasses provides no real benefit.
Design patterns should solve real problems. We should not add a pattern only to make the code look advanced.
25. Common Student Confusion
Is every method that creates an object a Factory Method?
No.
This is only an object-creation method:
public Notification createNotification() {
return new EmailNotification();
}
The Factory Method Pattern normally involves:
A parent Creator
A Factory Method
Child Creators overriding that method
Child Creators deciding which Product to instantiate
A common parent workflow using the returned Product
Does Factory Method remove the new keyword?
No. It places new inside the responsible Concrete Creator.
Who decides which object will be created?
The Concrete Creator decides.
Why does the Factory Method return Notification?
Because it may return any class that implements Notification.
26. Final Classroom Summary
Factory Method is a Creational Design Pattern.
It separates:
What work should be performed
from:
Which object should perform that work
The parent Creator manages the common workflow.
The child Creator decides which Concrete Product should be created.
This reduces direct dependency between business logic and Concrete Product classes.
Final definition
Factory Method defines a method for creating an object, but allows subclasses to decide which concrete object should be instantiated.
Golden line
The parent decides the process, while the child decides the Product.
Loose-coupling golden line
Loose coupling means depending on a common contract instead of strongly depending on one particular implementation.