In this week’s tip, we’ll look at the Singleton design pattern. It’s one of the simplest to understand, Most books start by examining this pattern, similar to Java books which start with the “Hello, world!” program.
Singleton Pattern as a Creational Pattern
Remember that the GoF identified three types of patterns – creational, structural and behavioural.
The Singleton is a creational pattern. These patterns deal with the best way to create instances of objects. Our programs can be more flexible and reusable if we isolate the details of the creational process into a “creator” class.
Singleton Pattern: Intent
Patterns are generally described by a number of important parameters: name, motivation, intent and applicability (what it does, which problem it solves, where it can be used), consequences (what are the trade-offs from using a particular pattern), participants, collaborations, implementation, etc.
It is important to know the intent of a pattern. The intent of the Singleton design pattern is to ensure that only one instance of a class is created. It provides a global point of access to this instance object. It is occasionally important that a class has only one instance. We would probably only have one event logger, one print spooler, one identification number generator, one invoice generator, etc.
Singleton Pattern: EventLogger Example
The classic example of the Singleton design pattern is logging, so let’s create a simple event logging class. It will have a number of logging methods for different types of events, but this aspect of the class is not important from the point of view of implementing the Singleton pattern.
public class EventLogger { public void info(String message); public void warning(String message); public void error(String message); public void log(String message); }
If we need only one instance of the EventLogger
in our application, one option would be to create a global variable and assign an instance of the EventLogger
to it. Creating a global variable is convenient if the programming language allows it (Java doesn’t). We would need to define rules for creating and using the global variable. These rules are difficult to enforce, and easy to get around. We should rather create a language mechanism to enforce this access.
The better solution is to use the Singleton design pattern. It has advantages over merely declaring a global variable. The Singleton pattern is conceptually very simple, and easy to code. Typically we would add just five or six statements to an existing Java class.
To turn a class into a singleton, we must do the following:
- Mark the constructor of the class as
private
. This prevents any clients from directly creating instances of the class. - Create a private static reference to an object of the class type.
- Create a public static
getInstance()
method to the class. This method returns an instance of the class. The first timegetInstance()
is called, an instance of the class is created, cached and returned. When thegetInstance()
method is subsequently called, the cached instance is returned.
Let’s see this in code:
public class EventLogger { private static EventLogger instance; // cached reference public static EventLogger getInstance() { if (instance == null) instance = new EventLogger(); // lazy instantiation return instance; } private EventLogger() { // appropriate code } // all other normal logging methods }
We would use this code as follows:
... EventLogger logger = EventLogger.getInstance(); logger.log("Some message"); ...
Thread-safe Singletons
We need to ensure that getInstance()
is thread-safe if we’re using this class in a multi-threaded application. We can add the synchronized
keyword to the getInstance()
method to make it thread-safe.
Synchronized methods are much slower than non-synchronized ones. However, unless getInstance()
is called repeatedly, the overhead will be negligible.
If the singleton will always be needed in the application, we can create the object instance at startup, and have a non-synchronized getInstance()
method as follows:
public class EventLogger { // eager instantiation private static EventLogger instance = new EventLogger(); public static EventLogger getInstance() { return instance; } private EventLogger() { // appropriate code } // all other normal logging methods }
The Java runtime guarantees that static class variables are initialized before they are accessible from any thread. Just remember that any data needed to initialize the instance must be available during static initialization.
Enum Singleton Example
The previous code is the “traditional” approach to coding singletons. Java has an enum
class which makes it much easier to code thread-safe singletons. The enum
constructor is implicitly private. An example follows:
public enum EventLogger { INSTANCE; // that's it! // all other normal logging methods }
We use this code as follows:
EventLogger.INSTANCE.log("Some message");
Internally enum
s consist of static class variables, which we now know are guaranteed to be initialized before they are accessible from any thread.
What’s next?
In the weeks ahead, we’ll look at the more useful design patterns. Stay tuned!