Factory Method Pattern
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. It lets a class defer instantiation to its subclasses.
This pattern is one of the most widely used in Java. It's central to the idea of dependency injection and is used heavily in frameworks like Spring to decouple the creation of objects from the code that uses them.
When to Use It
- When a class cannot anticipate the class of objects it must create.
- When a class wants its subclasses to specify the objects it creates.
- When you want to provide users of your library or framework with a way to extend its internal components.
Structure
- Product: Defines the interface for objects the factory method creates.
- ConcreteProduct: Implements the
Productinterface. These are the actual objects that will be created. - Creator (Factory): Declares the factory method, which returns an object of type
Product. The Creator may also define a default implementation of the factory method. - ConcreteCreator (Concrete Factory): Overrides the factory method to return an instance of a
ConcreteProduct.
Example: A Notification Service
Imagine a system that needs to send different types of notifications (SMS, Email, Push). Using the Factory Method pattern, we can create a generic NotificationService that relies on subclasses to create the specific notification sender.
1. Product Interface (Notification)
This is the common interface for all notification types.
package com.example.patterns.factory;
public interface Notification {
void send(String message);
}
2. Concrete Products (EmailNotification, SmsNotification)
These are the actual implementations of the Notification interface.
EmailNotification.java
package com.example.patterns.factory;
public class EmailNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending Email: " + message);
}
}
SmsNotification.java
package com.example.patterns.factory;
public class SmsNotification implements Notification {
@Override
public void send(String message) {
System.out.println("Sending SMS: " + message);
}
}
3. Creator Class (NotificationFactory)
This is an abstract class that defines the abstract createNotification() factory method.
package com.example.patterns.factory;
public abstract class NotificationFactory {
// This is the "Factory Method"
public abstract Notification createNotification();
/**
* This is a method in the creator that uses the product.
* It doesn't know which concrete product it's working with,
* as it relies on the subclass to create it.
*/
public void sendNotification(String message) {
Notification notification = createNotification();
notification.send(message);
}
}
4. Concrete Creators (EmailFactory, SmsFactory)
These subclasses implement the createNotification() method to produce specific notification objects.
EmailFactory.java
package com.example.patterns.factory;
public class EmailFactory extends NotificationFactory {
@Override
public Notification createNotification() {
return new EmailNotification();
}
}
SmsFactory.java
package com.example.patterns.factory;
public class SmsFactory extends NotificationFactory {
@Override
public Notification createNotification() {
return new SmsNotification();
}
}
5. Putting It All Together
Now, the client code can decide which type of notification to send by choosing the appropriate factory.
package com.example.patterns.factory;
public class FactoryDemo {
public static void main(String[] args) {
// The client code decides which factory to use.
NotificationFactory emailFactory = new EmailFactory();
emailFactory.sendNotification("This is an important project update!");
System.out.println("---");
NotificationFactory smsFactory = new SmsFactory();
smsFactory.sendNotification("Your verification code is 12345.");
}
}
Output:
Sending Email: This is an important project update!
---
Sending SMS: Your verification code is 12345.
This example shows how the client code is completely decoupled from the concrete EmailNotification and SmsNotification classes. It only needs to know which factory to instantiate to get the desired behavior.