Factory

Hide Which Class Is Created

A Factory method decides which concrete class to create, so callers depend only on an interface. Adding a new type changes the factory, not every caller.

Example

public class Main {
    public static void main(String[] args) {
        for (String channel : new String[]{"sms", "email", "push", "whatsapp"}) {
            try {
                Notifier n = NotifierFactory.create(channel);
                System.out.println(n.send("Your OTP is 4821"));
            } catch (IllegalArgumentException e) {
                System.out.println("Error: " + e.getMessage());
            }
        }
    }
}

interface Notifier { String send(String msg); }
class SmsNotifier implements Notifier { public String send(String m) { return "SMS   -> " + m; } }
class EmailNotifier implements Notifier { public String send(String m) { return "EMAIL -> " + m; } }
class PushNotifier implements Notifier { public String send(String m) { return "PUSH  -> " + m; } }

class NotifierFactory {
    static Notifier create(String channel) {
        return switch (channel.toLowerCase()) {
            case "sms" -> new SmsNotifier();
            case "email" -> new EmailNotifier();
            case "push" -> new PushNotifier();
            default -> throw new IllegalArgumentException("Unknown channel: " + channel);
        };
    }
}

Output

SMS   -> Your OTP is 4821
EMAIL -> Your OTP is 4821
PUSH  -> Your OTP is 4821
Error: Unknown channel: whatsapp

Static Factory Methods

Effective Java item 1: prefer static factory methods to constructors. They have names, can return cached objects or subtypes. Examples: List.of(), Optional.of(), Integer.valueOf() (cached -128..127), LocalDate.of(), Executors.newFixedThreadPool(). In Spring, the container itself is a giant factory (BeanFactory), and @Bean methods are factory methods.