Kohei Nozaki's blog 

The Singleton Pattern in Java


Posted on Tuesday Sep 22, 2020 at 12:47PM in Technology


In this entry, I’ll introduce some common ways to apply the Singleton Pattern in Java and a problem that can harm maintainability of your code base and a solution for the problem.

What is it?

The Singleton Pattern is a commonly used idiom to create and maintain objects called singletons. A singleton is the only instance of a class.

Common use cases are an object which keeps the user’s preferences in a desktop application, cache or a resource pool. One example would be a TCP connection pool where there are multiple active connections to a backend service like a database server or a web service. Typically there should be only one pool object which maintains all of the connections. Having multiple separate pool objects in one application doesn’t make much sense in most cases because it will make the use of the pool less effective.

As an example, let’s imagine that there is the following interface which allows us to fetch the weather data from some external web service:

interface WeatherFetcher {
    String fetchWeather(String cityCode) throws IOException;
    int fetchTemperature(String cityCode) throws IOException;
}

There are a couple of methods to fetch the weather (atmospheric conditions) and the temperature of a city. Since it involves network communcation, there can be an IOException.

Now imagine that you are working on implementing a feature which uses this interface. You are told that the performance requirement for this feature is strict, therefore you have decided to maintain a pool of active TCP connections to the external web service in order to keep the latency low. Applying the Singleton Pattern here sounds like a good idea because it will make sure that you will have only one pool object in your application.

Class with a private constructor

Let’s see how we can implement this with the Singleton Pattern. One simple implementation would be something like this:

public class SimpleSingleton implements WeatherFetcher {
    private static final SimpleSingleton INSTANCE = new SimpleSingleton();
    public static WeatherFetcher getInstance() { return INSTANCE; }
    private SimpleSingleton() { System.out.println("Populating the connection pool..."); }

    @Override
    public int fetchTemperature(String cityCode) {
        // gets an idle connection from the pool, sends a request
        return 71; // please assume it's from a response
    }

    @Override
    public String fetchWeather(String cityCode) {
        // gets an idle connection from the pool, sends a request
        return "Sunny"; // please assume it's from a response
    }

    // client code
    public static void main(String[] args) throws IOException {
        WeatherFetcher singleton = SimpleSingleton.getInstance();
        System.out.printf("Weather in New York: %s, %dF\n",
                singleton.fetchWeather("NewYork"), singleton.fetchTemperature("NewYork"));
    }
}

The class has a static final field called INSTANCE, where the only instance of this class is kept. There is a simple getter for that.

This class has a constructor populating the pool. Let’s imagine that it opens a lot of connections to an external web service and makes those ready to use. The constructor is private, which prevents creation of another instance of the class. Without this, any other class can create an instance of this class, which means that there is no guarantee that there is only one instance of this class. It also prevents inheritance because there is no constructor a subclass can call.

In order to get the singleton, the client needs to call the static method getInstance() first. After that, the client can fetch the weather data via the methods defined in the WeatherFetcher interface.

Class with a static holder class for lazy initialization

The SimpleSingleton class works fine, but there is one common requirement you might encounter. Populating the connection pool can be expensive and there might be a situation where the feature we just implemented will be used by only a very small number of your users, and you want to make sure that the connection pool gets populated only when it’s really needed.

We cannot guarantee that with the SimpleSingleton class. Let’s add a System.out.println() call to the main method and see what is happening there:

public static void main(String[] args) throws IOException {
    System.out.println("Doing some other stuff - I don't need the singleton yet"); // added
    WeatherFetcher singleton = SimpleSingleton.getInstance();
    System.out.printf("Weather in New York: %s, %dF\n",
            singleton.fetchWeather("NewYork"), singleton.fetchTemperature("NewYork"));
}

The main method yields the following output:

Populating the connection pool...
Doing some other stuff - I don't need the singleton yet
Weather in New York: Sunny, 71F

As you can see, the pool has got initialized even though it’s not needed yet; the constructor gets executed before the getInstance() method is called because the constructor invocation is written in the static initializer of the class. It can lead to a situation where unnecessary resource consumption is imposed on the user even though the user might not need the feature. We can work around this with having a simple private static holder class like the following:

public class LazySingleton implements WeatherFetcher {
    private static class SingletonHolder {
        static final LazySingleton INSTANCE = new LazySingleton();
    }
    public static WeatherFetcher getInstance() { return SingletonHolder.INSTANCE; }
    private LazySingleton() { System.out.println("Populating the connection pool..."); }
    ...
    // client code
    public static void main(String[] args) throws IOException {
        System.out.println("Doing some other stuff - I don't need the singleton yet");
        WeatherFetcher singleton = LazySingleton.getInstance();
        System.out.printf("Weather in New York: %s, %dF\n",
                singleton.fetchWeather("NewYork"), singleton.fetchTemperature("NewYork"));
    }
}

The LazySingleton singleton instance is kept in the static field in the static inner class called SingletonHolder and the getInstance() method returns that. Executing the main method will show the following output, as expected:

Doing some other stuff - I don't need the singleton yet
Populating the connection pool...
Weather in New York: Sunny, 71F

A problem that can harm maintainability: getInstance() everywhere

Let’s imagine that now you need to implement some code for generating a report about the weather of some cities with the singleton we just wrote. It might be implemented like this:

public class WeatherReporter {
    String generateWeatherReport(List<String> cityCodeList) {
        StringBuilder sb = new StringBuilder("=== Weather Report ===\n");
        for (String cityCode : cityCodeList) {
            try {
                String weather = LazySingleton.getInstance().fetchWeather(cityCode);
                int temperature = LazySingleton.getInstance().fetchTemperature(cityCode);
                sb.append(String.format("%s: %s, %dF\n", cityCode, weather, temperature));
            } catch (IOException e) {
                sb.append(String.format("%s: Failed to fetch data\n", cityCode));
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        WeatherReporter reporter = new WeatherReporter();
        System.out.println(reporter.generateWeatherReport(
                Arrays.asList("NewYork", "Montreal", "Tokyo")));
    }
}

It receives a List of city code which we need to create the report for. Then it constructs the header, iterates the list of city code and fetches the weather and the temperature for each city and builds the String which represents the report. If there is an I/O problem, we just mention that it failed to fetch the data for the city.

Executing the main method should produce the following output:

Populating the connection pool...
=== Weather Report ===
NewYork: Sunny, 71F
Montreal: Cloudy, 68F
Tokyo: Rainy, 69F

That’s a fairly complicated piece of code. Therefore, having some unit tests for the WeatherReporter class would be nice, but unfortunately it’s almost impossible because the current implementation is hard-coded to directly call the static getInstance() method to get the WeatherFetcher object, which means it always sends requests to the real external web service.

We cannot make a reliable assertion if it depends on the output of the real external service that way. One option would be running a fake of the external service, but it would require a lot of effort. And it would be nice if we could write a test case for IOException handling, but reproducing IOException in a real environment is also too much work. And the connection pool population might take a lot of time. The LazySingleton class might be hard-coded to create thousands of TCP connections and in that case it might take a few tens of seconds to populate. Waiting for such a long time with every unit test execution doesn’t sound reasonable.

So, the problem here is that we have business logic tightly coupled to an external data source. Having that kind of a method call in the middle of business logic makes writing unit tests very difficult or almost impossible because in order to make that kind of thing work, in many cases it requires some sort of specific setup or configuration and oftentimes that is too much work.

One thing we can try to work around this problem is replacing the static getInstance() method call by a functional interface. In our case, we can do something like this:

public class ImprovedWeatherReporter {

    private final Supplier<? extends WeatherFetcher> weatherFetcherSupplier;

    public ImprovedWeatherReporter(Supplier<? extends WeatherFetcher> weatherFetcherSupplier) {
        this.weatherFetcherSupplier = weatherFetcherSupplier;
    }

    String generateWeatherReport(List<String> cityCodeList) {
        StringBuilder sb = new StringBuilder("=== Weather Report ===\n");
        for (String cityCode : cityCodeList) {
            try {
                String weather = weatherFetcherSupplier.get().fetchWeather(cityCode);
                int temperature = weatherFetcherSupplier.get().fetchTemperature(cityCode);
                sb.append(String.format("%s: %s, %dF\n", cityCode, weather, temperature));
            } catch (IOException e) {
                sb.append(String.format("%s: Failed to fetch data\n", cityCode));
            }
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        ImprovedWeatherReporter reporter = new ImprovedWeatherReporter(LazySingleton::getInstance);
        System.out.println(reporter.generateWeatherReport(
                Arrays.asList("NewYork", "Montreal", "Tokyo")));
    }
}

The ImprovedWeatherReporter class is not tightly coupled to the getInstance() method anymore. Instead, now it has a field for a Supplier object which returns a WeatherFetcher object. The client of this class can inject the Supplier via its constructor. In our case, we can inject LazySingleton.getInstance() for the production use.

That seems to be not much change, but it is a great improvement from the perspective of unit testing. Now we can inject any Supplier returning a WeatherFeather object, and it enables us to write reliable, fast and stable unit tests like the following:

@ExtendWith(MockitoExtension.class)
class ImprovedWeatherReporterTest {

    @Mock
    WeatherFetcher weatherFetcher;
    ImprovedWeatherReporter sut;

    @BeforeEach
    void setUp() {
        sut = new ImprovedWeatherReporter(() -> weatherFetcher);
    }

    @Test
    void producesReports() throws IOException {
        when(weatherFetcher.fetchTemperature("NewYork")).thenReturn(71);
        when(weatherFetcher.fetchWeather("NewYork")).thenReturn("Sunny");
        when(weatherFetcher.fetchTemperature("Montreal")).thenReturn(68);
        when(weatherFetcher.fetchWeather("Montreal")).thenReturn("Cloudy");

        String actual = sut.generateWeatherReport(Arrays.asList("NewYork", "Montreal"));

        assertThat(actual).isEqualTo("=== Weather Report ===\n" +
                "NewYork: Sunny, 71F\n" +
                "Montreal: Cloudy, 68F\n");
    }

    @Test
    void exception() throws IOException {
        when(weatherFetcher.fetchTemperature("Tokyo")).thenThrow(new IOException("catch this"));

        String actual = sut.generateWeatherReport(Collections.singletonList("Tokyo"));

        assertThat(actual).isEqualTo("=== Weather Report ===\n" +
                "Tokyo: Failed to fetch data\n");
    }
}

First, we create a mock of WeatherFetcher and inject a Supplier which returns the mock. With that, we can have complete control of the WeatherFetcher object the ImprovedWeatherReporter class relies on. Now we can specify what the WeatherFetcher object returns for what parameter with the mock framework you use. It will even enable us to test a case where the IOException is thrown, which was impossible with the tightly-coupled version of the WeatherReporter class. Just having a simple abstraction layer between your business logic and an external service makes writing unit tests much easier.

Another solution you might want to check is using a dependency injection framework to maintain singletons. You can easily make an object singleton and let the framework inject the singleton to the objects the framework maintains. It will reduce a lot of boilerplates, and some of the code I introduced here will be unnecessary. Especially if your code base has a dependency injection framework already, I recommend checking the documentation of your framework. For Google Guice, check this out: https://github.com/google/guice/wiki/Scopes

Conclusion

We’ve seen a couple of common idioms that can be used to apply the Singleton Pattern in Java, and discussed a common maintainability issue that is sometimes caused by applying the pattern. When you see code where there are a lot of getInstance() calls in the middle of business logic, it might be good to take some time to see if you would be able to run your business logic in isolation for unit tests. If your code was unit test friendly, your code would be likely to have wider coverage of tests and it would make future maintenance easier and the code base more stable.