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

  1. Encapsulation of Object Creation – The instantiation logic is moved to a separate method.

  2. Loose Coupling – The client code depends on an abstract class/interface rather than concrete classes.

  3. Scalability – New types of objects can be added without modifying existing code.

  4. 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

  1. Product (NotificationService - Interface)

    • Defines the common behavior that all concrete classes (products) must implement.
  2. Concrete Products (EmailNotification, SmsNotification, PushNotification)

    • These are the actual objects that implement NotificationService.
  3. Factory (NotificationFactory)

    • Creates and returns the correct NotificationService object based on input.
  4. 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)

  1. Encapsulation of Object Creation

    • Main (client) does not need to know how EmailNotification, SmsNotification, or PushNotification objects are created.

    • It just requests a notification from the factory.

  2. 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 modifying Main.

  3. 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.

  4. Better Code Reusability

    • The same NotificationFactory can be used across multiple parts of the application.
  5. 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)

  1. 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.

  2. Performance Overhead

    • If the factory class grows too large, it can become a bottleneck.

    • A large number of if-else or switch cases can slow things down.

    • Alternative: Use a Map-Based Factory instead of if-else.

  3. 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.

  4. 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.