How do I implement the Factory Method pattern in Java?

The Factory Method pattern is a Gang of Four (GoF) creational design pattern. It defines an operation for creating a product while allowing subclasses to choose the concrete product returned. This lets a base class reuse a workflow without hard-coding the product implementation into that workflow.

This article continues our creational patterns series after Singleton. Here, we focus on choosing an implementation through subclassing.

The problem

Suppose an application sends notifications by email and SMS. Both channels support the same operation, send(), but each has its own implementation.

We want a notification service to follow a shared workflow: create a notification, then send the message. The shared workflow should work with either channel. Factory Method gives subclasses a creation step to customize.

A complete example

Save the following code as FactoryMethodExample.java. The classes are nested so that the entire example fits into one source file. In a larger application, you can place them in separate files.

package org.kodejava.pattern.factorymethod;

public class FactoryMethodExample {
    interface Notification {
        void send(String message);
    }

    static class EmailNotification implements Notification {
        @Override
        public void send(String message) {
            System.out.println("Email: " + message);
        }
    }

    static class SmsNotification implements Notification {
        @Override
        public void send(String message) {
            System.out.println("SMS: " + message);
        }
    }

    abstract static class NotificationService {
        protected abstract Notification createNotification();

        public void notifyUser(String message) {
            Notification notification = createNotification();
            notification.send(message);
        }
    }

    static class EmailNotificationService extends NotificationService {
        @Override
        protected Notification createNotification() {
            return new EmailNotification();
        }
    }

    static class SmsNotificationService extends NotificationService {
        @Override
        protected Notification createNotification() {
            return new SmsNotification();
        }
    }

    public static void main(String[] args) {
        NotificationService email = new EmailNotificationService();
        email.notifyUser("Your order has been shipped.");

        NotificationService sms = new SmsNotificationService();
        sms.notifyUser("Your verification code is 482731.");
    }
}

The channel implementations print messages to the console to keep the example self-contained. They do not contact an email provider or SMS gateway.

Compile and run from the directory containing the source file:

javac -d out FactoryMethodExample.java
java -cp out org.kodejava.pattern.factorymethod.FactoryMethodExample

Output:

Email: Your order has been shipped.
SMS: Your verification code is 482731.

How it works

The example has four roles:

GoF role Example Responsibility
Product Notification Declares the operation shared by all notifications.
Concrete products EmailNotification, SmsNotification Implement the channel behavior.
Creator NotificationService Declares the factory method and uses its result in a shared workflow.
Concrete creators EmailNotificationService, SmsNotificationService Override the factory method to choose a concrete product.

The factory method is createNotification(). The inherited notifyUser() method calls it and uses the returned Notification without needing to know its concrete class.

Java supports this structure through abstract methods and method overriding: each concrete service supplies the creation behavior declared by its abstract superclass. See Java’s documentation on abstract methods and classes.

The client still selects a concrete service in main(). The pattern moves product creation behind an overridable method; it does not eliminate the need to choose which service the application uses.

Adding another channel

To support push notifications, implement Notification in a new PushNotification class. Then extend NotificationService and override createNotification() to return that product. The existing notifyUser() workflow can remain unchanged.

This example creates a new product for each call. Factory Method itself does not require a new instance every time; an implementation can return a reusable product if its lifecycle and thread-safety requirements allow it.

Factory Method versus a simple factory

A utility such as NotificationFactory.create("email"), with a conditional selecting a concrete class, is commonly called a simple factory. That can be useful, but it does not demonstrate the subclass-based creation mechanism shown here.

In this example, the defining relationship is that the creator’s shared workflow invokes a factory method overridden by its subclasses. Merely naming a method create() does not establish that structure.

When should I use it?

Use Factory Method when a base class has useful shared behavior, but subclasses need to customize the products that behavior uses. It is especially suitable for extension points in frameworks or reusable libraries.

The tradeoff is additional classes and inheritance. If all you need is a small conditional that constructs an object, a simple factory may be sufficient. If callers can supply an already-created Notification, constructor injection may provide a simpler way to customize the behavior.

Next in this series: Abstract Factory, which focuses on creating families of related products.