DEV Community

Cover image for Factory Pattern | JAVA Design Patterns
Akhil Arjun
Akhil Arjun

Posted on

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

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

  1. Creational Patterns
  2. Structural Patterns
  3. Behavioral Patterns

Today we will talk about one of the first Creational Pattern" i.e. Factory Pattern.

Factory Pattern (Creational 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.

Factory Pattern

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.

Enough talk, lets dive into the code!
Enough Talk

Notification Interface

public interface Notification {
    public void sendNotification(NotificationModel notif);
}
Enter fullscreen mode Exit fullscreen mode

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.

NotificationModel

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Notification interface will be then implemented in different ways by the individual classes.

Let's see how we can do that.

EMailNotification

public class EMailNotification implements Notification {

    @Override
    public void sendNotification(NotificationModel notif) {
        System.out.println("E-Mail Notification is going out\n" + 
                     notif + 
                     "\n");

    }

}
Enter fullscreen mode Exit fullscreen mode

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

SMSNotification

public class SMSNotification implements Notification {

    @Override
    public void sendNotification(NotificationModel notif) {
        System.out.println("SMS Notification is going out\n" + 
                     notif + 
                     "\n");

    }

}
Enter fullscreen mode Exit fullscreen mode

PushNotification

public class PushNotification implements Notification {

    @Override
    public void sendNotification(NotificationModel notif) {
        System.out.println("Push Notification is going out\n" + 
                     notif + 
                     "\n");

    }

}
Enter fullscreen mode Exit fullscreen mode

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.

NotificationType

public enum NotificationType {
    SMS, EMAIL, PUSH
}
Enter fullscreen mode Exit fullscreen mode

Now finally, we will create our factory class, that will be responsible for spitting out necessary Notification class to the end-user.

NotificationFactory

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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.

Main.java

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(
                    "to.test@gmail.com", 
                    "from.test@gmail.com", 
                    "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);
    }
}
Enter fullscreen mode Exit fullscreen mode

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 to.test@gmail.com from from.test@gmail.com

Push Notification is going out
Hey There! is sent to VPA12345 from VPA90585749
Enter fullscreen mode Exit fullscreen mode

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.

Factory Pattern - Github

Base Design Pattern Project - Github

My days are fueled with coffees and only coffees. So I know, you know what we all should do 🤞


Buy Me A Coffee

Top comments (0)