Skip to content
On this page

Factory Method

Define an interface for creating objects, allowing subclasses to decide which class to instantiate. The Factory Method delays the instantiation of a class to its subclasses.

The Factory Method is a creational design pattern.

The purpose of the Factory Method is to separate the creation of objects from their usage, and the client always references the abstract factory and the abstract product:

┌─────────────┐      ┌─────────────┐
│   Product   │      │   Factory   │
└─────────────┘      └─────────────┘
       ▲                    ▲
       │                    │
┌─────────────┐      ┌─────────────┐
│ ProductImpl │◀─── │ FactoryImpl │
└─────────────┘      └─────────────┘

Let's consider a concrete example: suppose we want to implement a Factory that parses a string into a Number. We can define it as follows:

java
public interface NumberFactory {
    Number parse(String s);
}

With the factory interface in place, we can create an implementation class for the factory:

java
public class NumberFactoryImpl implements NumberFactory {
    public Number parse(String s) {
        return new BigDecimal(s);
    }
}

Here, the product interface is Number, and the actual product returned by NumberFactoryImpl is BigDecimal.

So, how does the client create NumberFactoryImpl? Typically, we define a static method getFactory() in the factory interface to return the actual subclass:

java
public interface NumberFactory {
    // Creation method:
    Number parse(String s);

    // Get factory instance:
    static NumberFactory getFactory() {
        return impl;
    }

    static NumberFactory impl = new NumberFactoryImpl();
}

In the client, we only interact with the factory interface NumberFactory and the abstract product Number:

java
NumberFactory factory = NumberFactory.getFactory();
Number result = factory.parse("123.456");

The caller can completely ignore the actual factory NumberFactoryImpl and the real product BigDecimal. The advantage of this approach is that it allows the code that creates products to change independently without affecting the caller.

Some students might ask: "Do we need such a complex factory for a simple parse() method?" In reality, most of the time we do not need an abstract factory. Instead, we use static methods to return products directly, such as:

java
public class NumberFactory {
    public static Number parse(String s) {
        return new BigDecimal(s);
    }
}

This simplified approach of using static methods to create products is known as the Static Factory Method. Static Factory Methods are widely used in the Java Standard Library. For example:

java
Integer n = Integer.valueOf(100);

Integer acts both as a product and a static factory. It provides a static method valueOf() to create Integer instances. What’s the difference between this approach and directly writing new Integer(100)? Let's look at the valueOf() method:

java
public final class Integer {
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
    ...
}

The advantage is that valueOf() may use new to create a new Integer instance, but it can also return a cached Integer instance directly. From the caller's perspective, there's no need to know the details of how Integer is created.

Tip

The Factory Method can hide the details of product creation and does not necessarily create a new product each time. It can return cached products to improve speed and reduce memory consumption.

If the caller directly uses Integer n = new Integer(100), it loses the possibility of using cache optimizations.

Another commonly used Static Factory Method is List.of():

java
List<String> list = List.of("A", "B", "C");

This static factory method accepts varargs and returns a List interface. It’s important to note that the product returned to the caller is always the List interface, without concern for its actual type. Even if the caller knows that the actual type of the List product is java.util.ImmutableCollections$ListN, they should not force a cast to the subclass, because the static factory method List.of() guarantees to return a List, but it could easily be modified to return a java.util.ArrayList. This is the Liskov Substitution Principle: returning any subclass that implements the interface satisfies the method’s requirements without affecting the caller.

Tip

Always reference interfaces rather than implementation classes. This allows subclasses to change without affecting the caller's code, promoting programming to abstractions wherever possible.

Similarly, when using MessageDigest, to create a specific digest algorithm, always use the static factory method getInstance(String):

java
MessageDigest md5 = MessageDigest.getInstance("MD5");
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");

The caller obtains product instances by specifying the product name. This approach not only simplifies the call but also ensures that the reference remains the abstract class MessageDigest.

Practice

Use a static factory method to implement the conversion of an integer similar to 20200202 into a LocalDate:

java
public class LocalDateFactory {
    public static LocalDate fromInt(int yyyyMMdd) {
        ...
    }
}

Summary

The Factory Method defines factory and product interfaces, deferring the creation of actual factories and products to subclass implementations. This allows the caller to interact only with the abstract factory and abstract product.

In practice, the simpler Static Factory Method is more commonly used, allowing internal optimizations in product creation.

Callers should hold references to interfaces or abstract classes whenever possible, avoiding references to concrete subclass types. This enables the Factory Method to switch different subclasses without affecting the caller's code.

Factory Method has loaded