21
Factory Pattern | JAVA Design Patterns
In this tutorial (and more to come) we will talk about Design patterns most commonly used in Java. I have searched a lot of tutorials to understand what these mean, and coming as a non computer science background kid, everything seemed too alien! Every tutorial I read was either too filled with jargons or unapologetically lame cliche examples.
So I took it upon myself to create real-world scenarios and to use the these patterns as a solution for them, so that I could get a better grasp of these.
Feel free to correct me if I am wrong, coz these are my interpretations of the design patterns and how to use them.
Design patterns are set of common ideas that can be used in a given condition to make sure the code-base is streamlined, re-usable and stable.
They act like blueprints of ideas that can be customized according the need of your project.
It is never a mandatory situation where we have to go for a particular design pattern when at times simple code would do the job.
According to the complexity of a design or scale of an application patterns are divided into three main categories
- Creational Patterns
- Structural Patterns
- Behavioral Patterns
Today we will talk about one of the first Creational Pattern" i.e. Factory Pattern.
Factory Pattern is used to create objects using an interface, where the end-user get's to decide what kind of object they want without actually worrying about creating them.
To make more sense of that, let's take a real world example:
Imagine a notification system, with multiple delivery types like E-Mail Notification, SMS Notification, Push Notification, etc.
Here all the systems are trying to do the same functionality of sending notification to the end-user. But they will have different implementation strategies to achieve this. Which is why I believe this will be a great candidate to explore Factory Pattern.
We will have a Notification interface built in which will be implemented by the individual types, to carry on with their functionality. And then we will create a factory that will return notification sub-types depending on the end-user selection.
public interface Notification {
public void sendNotification(NotificationModel notif);
}
This is the most important class of our project. This acts as the blue-print of our entire system. This class will have al the methods required by the end user like sendMessage(), getType(), getMsgInfo(), etc.
The interface right now has only one method and that is sendNotification(NotificationModel notif)
. Where NotificationModel
is a simple rudimentary POJO that will hold all the basic necessary information.
public class NotificationModel {
private String to;
private String from;
private String msg;
// Getters and Setters Here ...
public NotificationModel(String to, String from, String msg) {
this.to = to;
this.from = from;
this.msg = msg;
}
@Override
public String toString() {
return msg +
" is sent to " +
to +
" from " +
from;
}
}
Notification interface will be then implemented in different ways by the individual classes.
Let's see how we can do that.
public class EMailNotification implements Notification {
@Override
public void sendNotification(NotificationModel notif) {
System.out.println("E-Mail Notification is going out\n" +
notif +
"\n");
}
}
Right now all that our EmailNotification class is doing, is printing some info to the console. Along with all the notification pojo information. We will keep it that way to remain focused on the tutorial here.
Similarly, we will create SMSNotification and PushNotification sub-calsses too
public class SMSNotification implements Notification {
@Override
public void sendNotification(NotificationModel notif) {
System.out.println("SMS Notification is going out\n" +
notif +
"\n");
}
}
public class PushNotification implements Notification {
@Override
public void sendNotification(NotificationModel notif) {
System.out.println("Push Notification is going out\n" +
notif +
"\n");
}
}
We will also create a NotificationType
enum so that there will be ease for the end-user while using the factory to build out Notification sub-classes.
public enum NotificationType {
SMS, EMAIL, PUSH
}
Now finally, we will create our factory class, that will be responsible for spitting out necessary Notification class to the end-user.
public class NotificationFactory {
public Notification createNotification(NotificationType type) {
Notification notification;
switch(type) {
case EMAIL:
notification = new EMailNotification();
break;
case PUSH:
notification = new PushNotification();
break;
case SMS:
default:
notification = new SMSNotification();
break;
}
return notification;
}
}
Here, the user will pass a notification type which will determine which variant of the notification interface will be returned. And then using a unified method signature we can make the rest of implementation easy.
public class Main {
public static void main(String[] args) {
NotificationModel smsNotif =
new NotificationModel("1234567895",
"1234567899",
"Hey There!");
Notification smsNotification =
new NotificationFactory()
.createNotification(NotificationType.SMS);
NotificationModel emailNotif =
new NotificationModel(
"[email protected]",
"[email protected]",
"Hey There!");
Notification emailNotification =
new NotificationFactory()
.createNotification(NotificationType.EMAIL);
NotificationModel pushNotif =
new NotificationModel("VPA12345",
"VPA90585749",
"Hey There!");
Notification pushNotification =
new NotificationFactory()
.createNotification(NotificationType.PUSH);
smsNotification.sendNotification(smsNotif);
emailNotification.sendNotification(emailNotif);
pushNotification.sendNotification(pushNotif);
}
}
When we run this program this is the output we recieve in the console
SMS Notification is going out
Hey There! is sent to 1234567895 from 1234567899
E-Mail Notification is going out
Hey There! is sent to [email protected] from [email protected]
Push Notification is going out
Hey There! is sent to VPA12345 from VPA90585749
Note: when we look close to this example, we can understand this is nothing but an interface and its implementation strategy. And we use a class called factory to get the required instances. So, why go through all this pain?
We could let the customer decide the sub-class and call the required constructor, right?
Well, this could be the case, but as the application grows and number of types of notification grows, it becomes more and more complex for the end-user to keep a track. So the factory methodology becomes easier and abstract enough for the end-user to call our API's.
As our application grows, and becomes more and more complex. Repetitive patterns start emerging of problems and bottlenecks and we can use these blueprints of design patterns to solve those.
Hope, you all had a great time going through the article. Do let me know if it was helpful.
All the code we will discuss in this series will also be present in my github project.
21