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.

How do I use Map.of() factory method to create a map object?

In Java, the Map.of() factory method can be used to create an unmodifiable map of specified key-value pairs. This method is available in Java 9 and later versions.

Creating a map is a bit more complicated than creating lists or sets. Because we need to provide keys and values when creating a map. When using the Map.of() factory method we set the content of the map by alternating between the keys and values of the map.

Consider the following example:

package org.kodejava.util;

import java.util.Map;

public class MapOfExample {
    public static void main(String[] args) {
        Map<String, Integer> map = Map.of("John", 25, "Mary", 30, "Alice", 27, "Rosa", 22);

        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " : " + entry.getValue());
        }
    }
}

Output:

Rosa : 22
Mary : 30
John : 25
Alice : 27

In the example above, the Map.of("John", 25, "Mary", 30, "Alice", 27, "Rosa", 22) statement creates an unmodifiable map with three key-value pairs. After the map is created, any attempt to modify the map (add, update or remove elements) will throw an UnsupportedOperationException.

Note that Map.of() doesn’t accept null keys or values. If a null key or value is provided, then a NullPointerException is thrown. Besides, if duplicate keys are provided, an IllegalArgumentException is thrown.

The Map.of() method is overloaded to accept up to 10 key-value pairs. If there are more than 10 pairs, you can use Map.ofEntries() factory method to create a map. This is how we use it:

Map<String, Integer> map = Map.ofEntries(
    Map.entry("John", 25),
    Map.entry("Mary", 30),
    Map.entry("Alice", 27),
    Map.entry("Bob", 32),
    // ...
);

for (Map.Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + " : " + entry.getValue());
}

The Map.entry() is another factory method provided to create Map.Entry object.

How do I use Set.of() factory method to create a set object?

As with List.of(), in Java 9, the Set.of() factory method can be used to create an unmodifiable set of specified elements.

Here is a simple example:

package org.kodejava.util;

import java.util.Set;

public class SetOfExample {
    public static void main(String[] args) {
        Set<String> names = Set.of("Rosa", "John", "Mary", "Alice");

        for (String name : names) {
            System.out.println(name);
        }
    }
}

Output:

John
Rosa
Alice
Mary

In this example, the Set.of("Rosa", "John", "Mary", "Alice") statement creates an unmodifiable set of strings containing “Rosa”, “John”, “Mary”, and “Alice”. The resulting set is unmodifiable, so attempting to add, update, or remove elements from it will throw an UnsupportedOperationException.

If you try to create a Set by providing a duplicate elements, an IllegalArgumentException will be thrown. A Set is a type of collection container that cannot have duplicate values in it.

Note that the Set.of() method doesn’t accept null values. If you try to insert a null value, it will throw a NullPointerException. If you add a null value using the add() method UnsupportedOperationException will be thrown.

Set.of() is overloaded similarly to List.of(), allowing you to create a set with varying numbers of elements. The below examples demonstrate the use of Set.of() with different numbers of arguments:

Set<String> a = Set.of(); // An empty set
Set<String> b = Set.of("One"); // A set with one element
Set<String> c = Set.of("One", "Two"); // A set with two elements
// ...
Set<String> j = Set.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"); // A set with ten elements

If you need to create a set with more than 10 elements, Set.of() offers an overloaded version that accepts an array or varargs:

Set<String> set = Set.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven");

Remember that sets created with Set.of() are unmodifiable. Attempting to add, remove or change an element in these sets after their creation causes an UnsupportedOperationException.

Also, Set.of() doesn’t allow duplicate or null elements. If you pass duplicate or null values, it will throw IllegalArgumentException and NullPointerException respectively.

How do I use List.of() factory method to create a list object?

In Java, you can use the List.of() factory method to create an unmodifiable List consisting of specified elements. This method is available from Java 9 onwards.

Here is a simple example:

package org.kodejava.util;

import java.util.List;

public class ListOfExample {
    public static void main(String[] args) {
        List<String> names = List.of("Rosa", "John", "Mary", "Alice");

        for (String name : names) {
            System.out.println(name);
        }

        names.add("Bob"); // throws java.lang.UnsupportedOperationException
    }
}

In the code above, we have created a list of names including “Rosa”, “John”, “Mary”, and “Alice”. This newly created list is unmodifiable, so attempting to add, update, or remove elements from it will throw an UnsupportedOperationException.

There are several overloaded versions of the List.of() method that each accept different numbers of arguments. The versions range from no argument (which creates an empty list) to 10 explicit arguments of type E. Here’s an example:

List<String> a = List.of(); // An empty list
List<String> b = List.of("One"); // A list with one element
List<String> c = List.of("One", "Two"); // A list with two elements
// ...
List<String> j = List.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten"); // A list with ten elements

However, if we need to create a list with more than 10 elements, we have another overloaded version of List.of() method which accepts an array or varargs.

List<String> list = List.of("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven");

Remember that these lists are unmodifiable. That means, if you try to modify the list (add, update, or remove elements) after they have been created, an UnsupportedOperationException will be thrown. Also, List.of() doesn’t allow null elements. If you pass null, it will throw UnsupportedOperationException.

How do I create beans through factory method?

This example show you how to create a bean using factory method in Spring Framework. We will use a singleton as an example. An instance of a singleton can be obtained only from a factory method because a singleton will have a private constructor, so it will not be possible to create an instance of this class from outside the singleton itself.

Here is our singleton bean will look like. To get an instance of it we will need to call the getInstance() method.

package org.kodejava.spring.core;

public class Singleton {

    /**
     * Private constructor.
     */
    private Singleton() {
    }

    /**
     * Get an instance of Singleton class.
     *
     * @return an instance of Singleton class.
     */
    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    /**
     * Do something.
     */
    public void doSomething() {
        System.out.println("Singleton.doSomething");
    }

    private static class SingletonHolder {
        static Singleton instance = new Singleton();
    }
}

Next, we will create a spring configuration file. At it simplest a bean in the configuration file will have an id and a class. To tell the spring container to create the bean using a factory method we must use the factory-method attribute of the bean element.

Here is our configuration file and we name it factory-method.xml.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="singleton" class="org.kodejava.spring.core.Singleton" factory-method="getInstance"/>

</beans>

Let’s now run our program below:

package org.kodejava.spring.core;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class FactoryMethodDemo {
    public static void main(String[] args) {
        try (ClassPathXmlApplicationContext context =
                     new ClassPathXmlApplicationContext("factory-method.xml")) {
            Singleton instance = (Singleton) context.getBean("singleton");
            instance.doSomething();
        }
    }
}

Maven Dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>5.3.23</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>5.3.23</version>
    </dependency>
</dependencies>

Maven Central Maven Central Maven Central