Exploring the Singleton Design Pattern
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |
Mark Townsend
Microsoft Corporation
February 2002
Summary: Discusses the Singleton design pattern, a creational pattern to dictate how and when objects get created, and its effective use with the Microsoft .NET Framework. (9 printed pages)
Contents
Introduction
Singleton Pattern
Conclusion
Introduction
In the course of developing software applications, repetitive patterns emerge as the application is developed. As whole software systems are developed, many of these same patterns become evident at scale.
This concept of repetitive pattern is evident in other applications. One such application is automobile manufacturing. Many different automobile models share the same sub-assemblies from the most basic components, such as light bulbs and fasteners, to larger assemblies, such as chassis and engines.
In homebuilding, the repetitive pattern concept applies to screws and nails as well as to whole building electrical distribution systems. Whether a team is assembled to create a new automobile or a new building design, it usually does not have to ponder problems that were solved before. If a team assigned to design and build a house had to rethink and design every single component of the house, the whole process would take much longer than it currently does. Design decisions such as the height of a door or the function of a light switch are well understood. A house designer does not have to redesign and reconstruct a different type of device to deliver and collect water in order to meet a requirement to supply hand washing functionality to different parts of the house: a standard sink, as well as a standard interface of hot and cold water inputs and drain water output, are well understood components of house construction. The repetitive pattern concept can be applied over and over to nearly everything we surround ourselves with, including software.
The automobile and homebuilding examples help visualize some general abstract concepts in software design and construction. The concept of well-defined general purpose units of functionality that are well understood is the motivation for design patterns and the focus of two other design pattern articles, Exploring the Factory Design Pattern and Exploring the Observer Design Pattern. These patterns cover nearly every aspect of object-oriented software design including object creation, object interaction, and object lifetime. For the scope of this article, we are going to discuss the Singleton pattern, which lives in a family of creational patterns.
Creational patterns dictate how and when objects get created. Many instances require special behavior that can only be solved though creational techniques, rather than trying to force a desired behavior after an instance is created. One of the best examples of this type of behavioral requirement is contained in the Singleton pattern. The Singleton pattern was formally defined in the classic reference, Design Patterns: Elements of Reusable Software by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (also known as the Gang of Four, or GoF). This pattern is one of the least complicated, as well as most popular, in Design Patterns. However, as we will see, there can be problems with implementing this pattern. This article attempts to examine the Singleton pattern from its beginning though various early implementations, as well as how to best use it in Microsoft® .NET application development.
Singleton Pattern
The intent of the Singleton pattern as defined in Design Patterns is to "ensure a class has only one instance, and provide a global point of access to it".
What problem does this solve, or put another way, what is our motivation to use it? In nearly every application, there is a need to have an area from which to globally access and maintain some type of data. There are also cases in object-oriented (OO) systems where there should be only one class, or a predefined number of instances of a class, running at any given time. For example, when a class is being used to maintain an incremental counter, the simple counter class needs to keep track of an integer value that is being used in multiple areas of an application. The class needs to be able to increment this counter as well as return the current value. For this situation, the desired class behavior would be to have exactly one instance of a class that maintains the integer and nothing more.
At first glance, one might be tempted to create an instance of a counter class as a just a static global variable. This is a common technique but really only solves part of the problem; it solves the problem of global accessibility, but does nothing to ensure that there is only one instance of the class running at any given time. The responsibility of having only one instance of the class should fall on the class itself and not on the user of the class. The users of the class should always be free from having to monitor and control the number of running instances of the class.
What is needed is a way to control how class instances are created and then ensure that only one gets created at any given time. This would give us exactly the behavior we require and free a client from having to know any class details.
Logical Model
The model for a singleton is very straightforward. There is (usually) only one singleton instance. Clients access the singleton instance through one well-known access point. The client in this case is an object that needs access to a sole instance of a singleton. Figure 1 shows this relationship graphically.
Figure 1. Singleton pattern logical model
Physical Model
The physical model for the Singleton pattern is also very simple. However, there are several slightly different ways that singletons have been implemented over time. Let's look at the original GoF singleton implementation. Figure 2 shows a UML model of the original Singleton pattern as defined in Design Patterns.
Figure 2. Singleton pattern physical model from design patterns
What we see is a simple class diagram showing that there is a private static property of a singleton object as well as public method Instance() that returns this same property. This is really the core of what makes a singleton. The other properties and methods are there to show additional operations that may be allowed on the class. For the purpose of this discussion, let's focus on the instance property and method.
Clients access any instance of a singleton only through the Instance method. How the instance gets created is not defined here. What we also want to be able to do is control how and when an instance will get created. In OO development, special object creation behavior is generally best handled in the constructor for a class. This case is no different. What we can do is define when and how we construct a class instance and then keep any client from calling the constructor directly. This is the approach always used for singleton construction. Let's look at the original example from Design Patterns. The C++ Singleton Sample Implementation Code example shown below is generally considered the default implementation for a singleton. This sample has been ported to many other programming languages and generally exists everywhere in very near this same form.
C++ Singleton Sample Implementation Code
// Declaration
class Singleton {
public:
static Singleton* Instance();
protected:
Singleton();
private:
static Singleton* _instance;
}
// Implementation
Singleton* Singleton::_instance = 0;
Singleton* Singleton::Instance() {
if (_instance == 0) {
_instance = new Singleton;
}
return _instance;
}
Let’s examine this code for a moment. This simple class has one member variable and that is a pointer to itself. Notice that the constructor is protected and that the only public method is the Instance method. In the implementation of the Instance method, there is a control block (if) that checks to see if the member variable has been initialized, and if not creates a new instance. This lazy initialization in the control block means that the Singleton instance is initialized, or created, only on the first call to the Instance() method. For many applications, this approach works just fine. But, for multithreaded applications, this approach proves to have a potentially hazardous side effect. If two threads manage to enter the control block at the same time, two instances of the member variable could be created. To solve this, you might be tempted to merely place a critical section around the control block in order to guarantee thread safety. If you do this, then all calls to the Instance method would be serialized and could have a very negative impact on performance, depending on the application. It is for this reason that another version of this pattern was created that uses something called a double-check mechanism. The next code sample shows an example of a double-check lock using Java syntax.
Double-Check Lock Singleton Code Using Java Syntax
// C++ port to Java
class Singleton
{
public static Singleton Instance() {
if (_instance == null) {
synchronized (Class.forName("Singleton")) {
if (_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
protected Singleton() {}
private static Singleton _instance = null;
}
In the Double-Check Lock Singleton Code Using Java Syntax sample, we perform a direct port of the C++ code to Java code in order to take advantage of the Java critical section block (synchronized). The major differences are that there are no longer separate declaration and implementation sections, there are no pointer data types, and a new double-check mechanism is in place. The double check occurs at the first IF block. If the member variable is null, then the execution enters a critical section block where the member variable is double checked again. Only after passing this last test is the member variable instantiated. The general thinking is that there is no way that two threads can create two instances of the class using this technique. Also, since there is no thread blocking at the first check, most calls to this method would not get the performance hit of having to enter the lock. Currently, this technique is widely used in many Java applications when implementing a Singleton pattern. This technique is subtle but flawed. Some optimizing compilers can optimize out or reorder the lazy initialization code and reintroduce the thread safety problem. For a more in-depth explanation, see "The Double-Check Locking is Broken" Declaration.
Another way to attempt to fix this problem could be using the volatile
keyword on the member variable declaration. This should tell the compiler to not reorder the code and forgo optimization. Currently this is only a proposed memory model for a JVM and does not solve the problem right now.
What is the best way to implement a singleton? It turns out, and not by accident, that the Microsoft .NET Framework has addressed all of these issues, thus making it easier to implement a singleton without the adverse side effects we discussed thus far. The .NET Framework, along with the C# language, allows us to port the preceding Java syntax to C# syntax by substituting language keywords where appropriate. So the singleton code becomes the following:
Double-Check Lock in C#
// Port to C#
class Singleton
{
public static Singleton Instance() {
if (_instance == null) {
lock (typeof(Singleton)) {
if (_instance == null) {
_instance = new Singleton();
}
}
}
return _instance;
}
protected Singleton() {}
private static volatile Singleton _instance = null;
}
Here we have substituted the lock keyword to perform the critical section block, used the typeof
operation, and added the volatile
keyword to ensure that there is no optimizer reordering of the code. Although this code is more or less a direct port of the GoF Singleton pattern, it does achieve our goals and we get the behavior we want. This code also illustrates some similarities as well as key differences in porting C++ to Java to C# code. However, as with any port of code, there are usually some advantages of the target language or platform that may be lost in the porting process. What needs to happen is a refactoring of the code in order to take advantage of the new target language or platform capabilities.
In each preceding code example, the original implementation for a singleton has undergone changes over time to address issues that have been discovered with each new implementation of the pattern. Issues such as thread safety have required changes to most implementations in order to account for the increasing demands, as well as evolutionary advances, in modern applications. .NET provides an evolutionary step in application development. Many of the issues that had to be accounted for in the preceding examples can be addressed at the Framework level and not at the implementation level. Although the last example shows a working Singleton class using the .NET Framework and C#, this code can be greatly simplified just by taking better advantage of the .NET Framework itself. The following sample uses .NET, is a minimal Singleton class based loosely on the original GoF pattern, and still gets similar behavior.
.NET Singleton Example
// .NET Singleton
sealed class Singleton
{
private Singleton() {}
public static readonly Singleton Instance = new Singleton();
}
This version is dramatically simpler and more intuitive. Is it still a singleton? Let's look at what changed and then decide. We modified the class itself to be sealed (non-inheritable), the lazy initialization code is removed, the Instance() method is removed, and we modified the _instance variable extensively. The changes to the _instance variable include modifying the access level to public, marking the variable as read-only, and initializing the variable at declaration time. Here we can directly define the behavior we want and not be concerned with potential unwanted side effects of the implementation. So what about the advantages of using lazy initialization and the hazards of multiple threads? All of the correct behaviors are built into the .NET Framework. Let's take the first case, lazy initialization.
The main reasons for using lazy initialization initially were to get the behavior of having an instance created on only the first call to the Instance() method, and because there was some openness in the C++ spec that did not define the exact initialization order of static variables. To get desired singleton behavior in C++, a workaround that involved the use of lazy initialization was necessary. What we really care about is that we get the instance created either on or just before the first call to (in this case) the Instance property, and that we have a defined initialization order for static variables in a class. With the .NET Framework, this is exactly the behavior we get. The Framework, during the JIT process, will initialize the static property when (and only when) any method uses this static property. If the property is not used, then the instance is not created. More precisely, what happens during JIT is that the class gets constructed and loaded when any static member of the class is used by any caller. In this case the result is the same.
What about thread-safe initialization? The Framework addresses this too. The Framework internally guarantees thread safety on static type initialization. In other words, in the example above, there is only one instance that would ever be created of the Singleton class. Note also that the property field used to hold the instance of the class is called Instance. This choice better illustrates that this value is an instance of the class as part of the discussion in this article. In the Framework itself there are several classes that use this type of singleton, although the property name used is called Value instead. The concept is exactly the same.
The rest of the changes to the class are meant to disallow sub-classing. Adding the sealed class modifier ensures that this class will not be sub-classed. The GoF Singleton pattern details the issues with trying to subclass a singleton and it is generally not a trivial matter. In most cases, it is very easy to develop a singleton without a parent class, and adding sub-classing functionality adds a new level of complexity that is generally not needed anyway. As complexity increases, so does the time required for testing, training, documenting, and so on. As a general rule, you never want to make any code more complicated than it absolutely has to be.
Let's look at a how a singleton might be used. Using our original motivational concept of a counter, we can create a simple Singleton counter class and show how we would use it. Figure 3 shows what the UML class description would look like.
Figure 3. UML Class Diagram
The corresponding class implementation code, as well as sample client usage, is shown below.
Sample Singleton Usage
sealed class SingletonCounter {
public static readonly SingletonCounter Instance =
new SingletonCounter();
private long Count = 0;
private SingletonCounter() {}
public long NextValue() {
return ++Count;
}
}
class SingletonClient {
[STAThread]
static void Main() {
for (int i=0; i<20; i++) {
Console.WriteLine("Next singleton value: {0}",
SingletonCounter.Instance.NextValue());
}
}
}
Here we created a Singleton class that maintains an incremental count with a type long
. The client is a simple console application that displays twenty values of the counter class. Although this sample is extremely simple, it does show how a singleton could be implemented using .NET and then used in an application.
Conclusion
The Singleton design pattern is a very useful mechanism for providing a single point of object access in an object-oriented application. Regardless of the implementation used, the pattern provides a commonly understood concept that can be easily shared among design and development teams. However, as we have discovered, it is also important to note how different these implementations can be and their potential side effects. The .NET Framework goes a long way to help an implementer of a pattern design the type of functionality desired without having to deal with many of the side effects discussed in this article. The validity of the pattern's original intent is proven when implemented properly.
Design patterns are very useful software design concepts that allow teams to focus on delivering the very best type of applications, whatever they may be. The key is to make proper and effective use of design patterns, and the ongoing MSDN series of articles on using design patterns with Microsoft .NET shows you how.
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. |