Design pattern - The Singleton
Table of Contents
1 - About
The singleton pattern is a design pattern used to implement the mathematical concept of a singleton, by restricting the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.
Singletons usually are used to provide a single point of access to a global service. It doesn't have to instantiate the object. It just has to provide access to the object. The object returned can be set by any means necessary.
2 - Articles Related
3 - Example
3.1 - Database
Application uses database connection pooling, so one DB connection can be shared among many requests. The singleton is usually the object which manages this pool.
4 - Instantiation
A singleton with parameters is NOT a singleton because you may have more object than one of them.
If you want your singleton to be initialized with some data, you may pass them after initialization (or via a builder pattern)
SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data
5 - Anti-Pattern
Singletons are an anti-pattern. They have private constructor and they are then very hard to subclass and configure.
6 - Implementation Type
6.1 - Static (Language-specific)
You can implement singletons by using language-specific features, such as the static keyword for Java
6.2 - Object
A singleton allows access to a single created instance that can be:
- passed as a parameter to other methods,
- treated as a normal object.
- and therefore can implement interface
7 - Object Implementation
From the less desirable to the most desirable solution.
7.1 - Method Initialization
public class MethodInitializationSingleton {
private static MethodInitializationSingleton singleton;
private MethodInitializationSingleton() {
}
public static MethodInitializationSingleton getSingleton() {
if (singleton == null) {
singleton = new MethodInitializationSingleton();
}
return singleton;
}
}
This implementation is not Thread-safe. If two threads, Thread 1 and Thread 2, call getInstance() at the same time, two instances can be created.
If Thread 1 is pre-empted just after it enters the if block and control is subsequently given to Thread 2.
7.2 - Class initialization
Class initialization (ie Eager initialization) with a static initialization block in order to manage exception.
public class ClassInitializationSingleton {
private static final ClassInitializationSingleton singleton;
static {
try {
singleton = new ClassInitializationSingleton();
} catch (Exception e) {
throw new RuntimeException("An error occurred!", e);
}
}
private ClassInitializationSingleton() {
}
public static ClassInitializationSingleton getSingleton() {
return singleton;
}
}
- Class initialization is thread-safe because of the Java specification.
- It is possible to instantiate more than one Singleton instance using Java reflections (by modifying the constructor to Accessible and calling it)
7.3 - Synchronized Method Initialization
public class SynchronizedSingleton {
private static SynchronizedSingleton singleton;
private SynchronizedSingleton() {
}
public static synchronized SynchronizedSingleton getSingleton() {
if (singleton == null) {
singleton = new SynchronizedSingleton();
}
return singleton;
}
}
- Synchronized methods are quite costly.
- It is possible to instantiate more than one Singleton instance using Java reflections (by modifying the constructor to Accessible and calling it)
7.4 - Enumeration
In Java, Enumerations can contain method implementations and hence can be used to implement Singleton’s logic.
See Enum Singleton
8 - Java
- Collections.singleton - an immutable set containing only the specified object.