Singleton Applied
Of course you know the Singleton pattern from the GoF book. But when it comes to implementation you sometimes feel unsure how to properly code one.
When and how to create the instance?
Implicitly with the instance() method | |
public final class ConnectionPool { private static ConnectionPool instance = null; private ConnectionPool() { .. do some initialization .. } public static synchronized ConnectionPool instance() { if (instance == null) { instance = new ConnectionPool(); } return instance; } ... } |
- synchronization and if-statement performance penalty to achieve thread-safety - moderate control over the the initialization time - re-initialization possible (by adding a reset() method - strongly discouraged |
At class load time | |
public final class Cache { private static Cache instance = new Cache(); private Cache() { } public static Cache instance() { return instance; } ... } |
- exact time when the constructor is called is not well defined, so don't rely on it - fast and thread-safe - re-initialization possible (by adding a reset() method) - seen often and proven useful |
Explicitly with a init() method | |
public final class ConnectionPool { private static ConnectionPool instance = null; private ConnectionPool() { .. do some initialization .. } public static void init() { instance = new ConnectionPool(); } public static ConnectionPool instance() { return instance; } ... } |
- total control over the initialization time - fast - not thread safe during initialization - re-initialization is possible through subsequent calls to the init() method - strongly encouraged |
static methods vs. instance() indirection
static methods |
public final class ConnectionPool { private static ConnectionPool instance = null; private Stack connections; ... public static Connection getConnection() { return (Connection) instance.connections.pop(); } public static void returnConnection(Connection conn) { instance.connections.push(conn); } } |
- less verbous application code: Connection conn = ConnectionPool.getConnection(); - more verbous singleton code: instance.connections.pop() - more convenient for the user - The class should be declared final as it makes no sense1 to
extend a class that has only static methods. Extending singletons is very questionable design anyway.
You are still able to implement any interface though.
|
instance() indirection |
public class ConnectionPool { private static ConnectionPool instance = null; private Stack connections; ... public static Cache instance() { return instance; } public Connection getConnection() { return (Connection) connections.pop(); } public void returnConnection(Connection conn) { connections.push(conn); } } |
- more verbous application code: Connection conn = ConnectionPool.instance().getConnection(); - less verbous singleton code: connections.pop(); - more convenient for the singleton programmer - It is still possible to extend the class even tough extending singletons is very questionable design. |
Monostate
A Monostate object hasstatic
member variables only. So all instances of a Monostate class share
the same state. This is a really rare pattern and it's use seems quite limited.
not quite a Singleton | |
public class Statistics { private static int requestCount = 0; public Statistics() { } public synchronized int getRequestCount() { return requestCount; } public synchronized int countRequest() { requestCount++; } } |
- do not perform initialization in the constructor! - behaviour very similar to a Singleton - can be very confusing for the application programmer, so clear documentation is a must. - can be extended but may loose its monostate character - be careful about thread safety and possible dead locks |
re-initialization and releasing resources
In a singleton you should always provide possibilities toa) re-initialize the singleton from scratch
b) release all resources held by the singleton
This is essential to be able to set a well-defined fixture for unit tests, software using self-healing mechanisms2, for software that dynamically loads and disposes components, and last but not least for many applications that are supposed to be running 24 hours a day.
Using explicit initialization with a init() method you get re-initialization completely for free.
re-initialization enabled singleton | |
public final class ConnectionPool { private static ConnectionPool instance = null; private Stack connections; private ConnectionPool() { connections = new Stack(); } public static void init() { instance = new ConnectionPool(); } ... } |
- Perform all initialization in the constructor. - Call init() to re-initialize the singleton but do it in a thread-safe way. |
dispose enabled singleton | |
public final class ConnectionPool { private static ConnectionPool instance = null; private Stack connections; private ConnectionPool() { connections = new Stack(); } public static void init() { instance = new ConnectionPool(); } public static void dispose() { instance = null; } ... } |
- In the dispose method throw away the whole instance and not just the state. - If you only want to clear internal state use re-initialization instead. - The init() method must be called before you can use the singleton again. |
1 Static methods can not be overwritten. A call to a static method is dependent on the type of the identifier and not on the object's class.
2 Self-healing here means to periodically clear internal state and re-initialize parts of an application. This can be used e.g. to handle unspotted memory leaks.