Factory Method UML Diagram, AI generated



// Step 1 — Interface

interface Notification {
   
void notifyUser();
}

// Step 2 — Implementations

class EmailNotification implements Notification {

   
@Override
   
public void notifyUser() {
        System.
out.println("Sending Email Notification");
    }
}

class SMSNotification implements Notification {

   
@Override
   
public void notifyUser() {
        System.
out.println("Sending SMS Notification");
    }
}

class PushNotification implements Notification {

   
@Override
   
public void notifyUser() {
        System.
out.println("Sending Push Notification");
    }
}

// Step 3 — Factory Class

/*

class NotificationFactory {

    public static Notification createNotification(String type) {

        if(type == null) {
            return null;
        }

        if(type.equalsIgnoreCase("EMAIL")) {
            return new EmailNotification();
        }

        if(type.equalsIgnoreCase("SMS")) {
            return new SMSNotification();
        }

        return null;
    }
}
*/




/*class NotificationFactory {

    private static final Map<String, Supplier<Notification>> notificationMap =
            Map.of(
                    "EMAIL", EmailNotification::new,
                    "SMS", SMSNotification::new,
                    "PUSH", PushNotification::new
            );

    public static Notification createNotification(String type) {

        Supplier<Notification> supplier =
                notificationMap.get(type.toUpperCase());

        if (supplier != null) {
            return supplier.get();
        }

        throw new IllegalArgumentException(
                "Invalid notification type: " + type);
    }
}*/

class NotificationFactory {

   
private static final ConcurrentHashMap<String,
                Supplier<Notification>>
registry =
           
new ConcurrentHashMap<>();

   
static {
       
registry.put("EMAIL", EmailNotification::new);
       
registry.put("SMS", SMSNotification::new);
       
registry.put("PUSH", PushNotification::new);
    }

   
public static Notification createNotification(String type) {

        Supplier<Notification> supplier =
               
registry.get(type.toUpperCase());

       
if (supplier != null) {
           
return supplier.get();
        }

       
throw new IllegalArgumentException(
               
"Invalid notification type: " + type);
    }

}

// Step 4 — Client Code

public class FactoryEmailSMS {

   
public static void main(String[] args) {

        Notification notification1 =
                NotificationFactory.createNotification(
"EMAIL");

        notification1.notifyUser();

        Notification notification2 =
                NotificationFactory.createNotification(
"SMS");

        notification2.notifyUser();

        Notification notification3 =
                NotificationFactory.createNotification(
"PUSH");

        notification3.notifyUser();
    }
}

 

 

Why factory instead of new?

Because:

·       object creation may become complex

·       creation logic may change

·       helps dependency inversion

Encapsulation: Hides the instantiation logic from the client.

Loose coupling - Client doesn't know concrete classes

Single Responsibility - Creation logic in one place

Open/Closed Principle - Add new types without changing existing code

Common Use Cases

·       Database connections - Different DB types (MySQL, PostgreSQL, MongoDB)

·       Logging frameworks - File, console, database loggers

·       Document parsers - PDF, Word, Excel parsers

·       UI frameworks - Different OS themes

·       Payment gateways - Multiple payment providers

The Factory Pattern is one of the most commonly used design patterns in Java frameworks like Spring, Hibernate, and JDBC!