Factory Design Pattern
Factory design pattern is a creational deisgn pattern, which provides an interface for creating objects but allows subclasses to alter the type of objects that will be created. it helps in achieving loose coupling and maintainability.
Key Concepts of Factory Method Pattern
Encapsulation of Object Creation – The instantiation logic is moved to a separate method.
Loose Coupling – The client code depends on an abstract class/interface rather than concrete classes.
Scalability – New types of objects can be added without modifying existing code.
Subclasses Override Factory Method – Subclasses can provide their own implementation of the factory method.
package DesignPatterns.Creational.Factory;
public interface NotificationService {
public void sendNotification(String msg);
}
package DesignPatterns.Creational.Factory;
public class EmailNotification implements NotificationService{
@Override
public void sendNotification(String msg) {
System.out.println("Sending Email Notification:" + msg);
}
}
package DesignPatterns.Creational.Factory;
public class SmsNotification implements NotificationService{
@Override
public void sendNotification(String msg) {
System.out.println("Senidng SmsNotification:" + msg);
}
}
package DesignPatterns.Creational.Factory;
public class PushNotification implements NotificationService{
@Override
public void sendNotification(String msg) {
System.out.println("Sending Push Notification:" + msg);
}
}
package DesignPatterns.Creational.Factory;
public class NotificationFactory {
public NotificationService getNotification(String notificationType) {
if (notificationType.equalsIgnoreCase("email")) {
return new EmailNotification();
} else if (notificationType.equalsIgnoreCase("sms")) {
return new SmsNotification();
} else if (notificationType.equalsIgnoreCase("push")) {
return new PushNotification();
} else {
throw new IllegalArgumentException("Invalid notification type: " + notificationType);
}
}
}
package DesignPatterns.Creational.Factory;
public class Main {
public static void main(String[] args) {
NotificationFactory notificationFactory = new NotificationFactory();
NotificationService notificationService = notificationFactory.getNotification("sms");
notificationService.sendNotification("Hello World");
notificationService = notificationFactory.getNotification("email");
notificationService.sendNotification("Hi Nivedita");
notificationService = notificationFactory.getNotification("push");
notificationService.sendNotification("New Update Available!");
}
}
Roles in the Factory Method Pattern
Product (
NotificationService
- Interface)- Defines the common behavior that all concrete classes (products) must implement.
Concrete Products (
EmailNotification
,SmsNotification
,PushNotification
)- These are the actual objects that implement
NotificationService
.
- These are the actual objects that implement
Factory (
NotificationFactory
)- Creates and returns the correct
NotificationService
object based on input.
- Creates and returns the correct
Client (
Main
class)The client does not create objects directly but requests them from the factory.
It calls the factory method and uses the returned object.
Advantages (Pros)
Encapsulation of Object Creation
Main
(client) does not need to know howEmailNotification
,SmsNotification
, orPushNotification
objects are created.It just requests a notification from the factory.
Loose Coupling
The client depends only on the interface (
NotificationService
), not concrete classes.If you add a new notification type (
WhatsAppNotification
), you only update the factory without modifyingMain
.
Easier Maintenance & Scalability
If you need to change how notifications are created (e.g., using a database instead of direct instantiation), you update only the factory class.
Adding new notification types (e.g.,
SlackNotification
) does not affect the client code.
Better Code Reusability
- The same
NotificationFactory
can be used across multiple parts of the application.
- The same
More Control Over Object Creation
You can log, cache, or restrict notification object creation inside the factory.
Example: Implementing a Singleton pattern to reuse the same object instead of creating new ones every time.
❌ Disadvantages (Cons)
Code Complexity Increases
Instead of directly using
new SmsNotification()
, now you have:A factory class
Extra if-else conditions
This might be unnecessary for small applications.
Performance Overhead
If the factory class grows too large, it can become a bottleneck.
A large number of
if-else
orswitch
cases can slow things down.Alternative: Use a Map-Based Factory instead of
if-else
.
Potential Violation of the Open-Closed Principle
Every time a new notification type is added (
SlackNotification
), the factory class must be updated.Alternative: Use Reflection API or Dependency Injection.
More Boilerplate Code
If you only have one or two notification types, using a factory might be overkill.
A simple direct instantiation (
new EmailNotification()
) might be enough.