Design Patterns: Singleton Pattern

These notes were adapted from various readings detailed in the References section.


The Singleton Pattern ensures a class has only one instance and provides a global point of access to it.

There are many objects in which we may only need a single instance of. For example, these objects can be purposed for thread pools, caches, UI windows, loggers, etc. In fact, there are some objects which can become problematic if another instance of it is active.

Fun fact, the Windows Task Manager deliberately ensures that there is only a single instance of it active.

The Singleton Pattern ensures that only one object can be instantiated for a given class. It works better than a global variable.

With a global variable, nothing is stopping a new object from being instantiated and then subsequently replaced by another instance of it through reassignment of that global variable. With a singleton, we can write the class in such a way in that attempting to retrieve a new instance results in an existing instance being returned instead.

To make this possible, we can leverage private constructors:

class Singleton {
    private static instance;
    
    private constructor() {
        instance = new Singleton();
    }
    
    public static getInstance() {
        if (!instance)
            instance = new Singleton()
            
        return instance;
    }
}

getInstance protects the client from initializing more than one instance of the singleton object. In fact, it is impossible to initialize the singleton outside of the Singleton class itself. The constructor is private!

We let our Singleton class manage an instance of itself, and prevent any other classes from creating a new instance of it. To retrieve an instance, we need to call a method from that singleton class to retrieve the managed instance.

Notice, that the method which retrieves the instance of the singleton must be made static, as it is impossible to instantiate an object from it. (Or whatever the version of static is in your favorite OOP)

It is assumed that the singleton is used with a single thread. With the above implementation and discussion so far, the approach to building a singleton will fail on multi-threaded environments. This is because multiple threads may call getInstances at the same time. In the case where instance = null, this will cause N number (where N is number of threads) new instances of that singleton to be created. Additional work must be done to ensure that only one thread enters the getInstance method at a time.

A quick way to get around this is to eagerly instantiate the instance variable upon loading of the class and not wait until getInstance is called. This modification results in the class:

class Singleton {
    private static instance = new Singleton();
    
    private constructor() {
        instance = new Singleton();
    }
    
    public static getInstance() {
        return instance;
    }
}

The approach depends on specific scenarios. When following the eager instantiation approach, consider performance when choosing this option.

The disadvantage of having a singleton is that it may be very difficult to test code which utilizes the singleton. This because a singleton removes control in deliberate construction of an instance. Thus, it is difficult to provide a mock object in test environments.

There are ways to solve this problem – such as having a singleton be received through dependency injection at some point. With that, your test environment may mock the entire singleton class and inject this implementation during test set up.

Overall, singletons can be tricky to create and manage, so it is best to really think about it over carefully before deciding to implement one in your system.

References‌

  • Head First Design Patterns - Eric Freeman & Elisabeth Freeman - https://www.oreilly.com/library/view/head-first-design/0596007124/