Using the Singleton Pattern
by Budi Kurniawan08/27/2003
The Singleton pattern is one of the simpler design patterns in the book
Design Patterns: Elements of Reusable Object-Oriented Software. This
pattern is effective for limiting the maximum number of instances of a class to
exactly one. In this case, if more than one object needs to use an instance of
the Singleton class, those objects share the same Singleton class instance. In
more advanced use, this pattern can also be used to manage exactly n
instances of a class. This article introduces this pattern with a simple
example and provides a real-world example by presenting the
StringManager class in Tomcat, the most popular servlet container.
All of the examples used in this article can be downloaded here.
Beginners and seasoned object-oriented programmers know that they create an
instance of a class by invoking the class' constructor preceded by the
new keyword. For example, the following code constructs an
instance of class MyClass by calling its no-argument
constructor:
new MyClass()
You get one object each time a constructor is called. If you call a class' constructor three times, you get three instances of the class. Even if you don't write a constructor in your class, the compiler creates a no-argument constructor for you. However, if a class does have a constructor, whether it is a no-argument constructor or not, the compiler does not create a new one. Normally, these constructors have a public access modifier because they are meant to be invoked from outside of the class.
However, there are cases where you want to limit the number of instances of
a class to one. For example, recall that in Microsoft Word you can press Ctrl-F
to display a Find dialog. However, for the whole life of Microsoft Word, there
can only be one Find dialog. If you press Ctrl-F two times, there is still only
one Find dialog. Even when there are multiple documents open, there can only be
one Find dialog that works with any active document. Indeed, you don't need
more than one instance of the Find dialog. Having more than one will probably
complicate matters. (Imagine having multiple instances of the Find dialog while
editing a document in Microsoft Word.)
|
Related Reading
Tomcat: The Definitive Guide |
The Singleton pattern can be used for this purpose. This pattern is effective for limiting the maximum number of instances of a class to exactly one. In this case, if more than one object needs to use an instance of the Singleton class, those objects share the same Singleton class instance. A class that implements the Singleton pattern is called a Singleton class.
How do you write a Singleton class? Simple: create a public static method that is solely responsible for creating the single instance of the class. This also means that the client of the class should not be able to invoke the Singleton class's constructor, either. Because the absence of a constructor will make the compiler create a no-argument public constructor, a class applying the Singleton pattern has a private or protected constructor. Because the constructor is private or protected, there is no way a client can create an instance of that class by calling its constructor. The constructor is not accessible from outside of the class!
If the only constructor cannot be accessed, how do we get an instance of that class? The answer lies in a static method in the class. As mentioned above, a Singleton class will have a static method that calls the constructor to create an instance of the class and return this instance to the caller of the static method. But isn't the constructor private? That's right. However, remember that the static method is also in the same class; therefore, it has access to all members of the class, including the private members of the class.
You might ask the following question: "You can't create an instance of a class by calling its constructor, so how do you call the static method (responsible for creating the single instance of the class) without having the class instance?" Note, though, that static members of a class can be invoked without having an instance of that class. To limit the number of instances to one, the static method has to check if an instance has been created before. If it has, it simply returns a reference to the previous created instance. If it has not, it calls the constructor to create one. It's as simple as that.
A Simple Example
As an example, consider a class called SingletonFrame in
Listing 1. This class is a Singleton class.
Listing 1. A Singleton class
package singleton;
import javax.swing.*;
public class SingletonFrame extends JFrame {
private static SingletonFrame myInstance;
// the constructor
private SingletonFrame() {
this.setSize(400, 100);
this.setTitle("Singleton Frame. Timestamp:" +
System.currentTimeMillis());
this.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
}
public static SingletonFrame getInstance() {
if (myInstance == null)
myInstance = new SingletonFrame();
return myInstance;
}
}
First of all, notice that the class only has one constructor and its access
modifier is private. Secondly, to get an instance of that class, you have the
static getInstance method, and there is also a static variable
called myInstance (of type SingletonFrame). The
getInstance method returns the myInstance variable.
The method checks if myInstance is null and, if so, calls the
constructor.
if (myInstance == null)
myInstance = new SingletonFrame();
return myInstance;
The method then returns myInstance.
To obtain a reference to the only instance of the SingletonForm
class, call the getInstance method, as in the following
snippet.
SingletonFrame singletonFrame =
SingletonFrame.getInstance();
Once you have an instance, you can call its public members just as you would
a normal class's object. For example, since SingletonFrame extends
the Frame class, you can call its setVisible
method.
The MyFrame class in Listing 2 shows how to use the
SingletonFrame class. MyFrame is a
JFrame with two buttons. Clicking one of the buttons creates an
instance of SingletonFrame. However, clicking either button again
will return the same instance.
Listing 2. Using the SingletonFrame
package singleton;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class MyFrame extends JFrame {
JButton jButton1 = new JButton();
JButton jButton2 = new JButton();
public MyFrame() {
try {
init();
}
catch(Exception e) {
e.printStackTrace();
}
}
private void init() throws Exception {
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
jButton1.setText("Show Singleton Frame");
jButton1.setBounds(new Rectangle(12, 12, 220, 40));
jButton1.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e) {
jButton1_actionPerformed(e);
}
});
jButton2.setText("Show the same Singleton Frame");
jButton2.setBounds(new Rectangle(12, 72, 220, 40));
jButton2.addActionListener(new java.awt.event.ActionListener()
{
public void actionPerformed(ActionEvent e) {
jButton2_actionPerformed(e);
}
});
this.getContentPane().setLayout(null);
this.getContentPane().add(jButton1, null);
this.getContentPane().add(jButton2, null);
}
void jButton1_actionPerformed(ActionEvent e) {
SingletonFrame singletonFrame = SingletonFrame.getInstance();
singletonFrame.setVisible(true);
}
void jButton2_actionPerformed(ActionEvent e) {
SingletonFrame singletonFrame = SingletonFrame.getInstance();
singletonFrame.setVisible(true);
}
public static void main(String[] args) {
MyFrame frame = new MyFrame();
frame.setSize(300, 250);
frame.setVisible(true);
}
}
Figure 1 shows MyFrame. If you click one of the buttons, the
SingletonFrame will be displayed (Figure 2). Note the timestamp on
the title of SingletonFrame.

Figure 1. The MyFrame frame

Figure 2. The SingletonFrame
Now close the SingletonFrame, then click a button on
MyFrame and notice that you get the same timestamp, indicating
that you get the same instance.
Now, let's look at the Singleton pattern used to maintain exactly n instances of a class.
Real-World Singleton Example: The StringManager Class in
Tomcat
A large application such as Tomcat needs to handle error messages carefully.
In Tomcat, error messages are useful for both system administrators and servlet
programmers. For example, Tomcat logs error messages in order for system
administrators to easily pinpoint any abnormality that has happened. For servlet
programmers, Tomcat sends a particular error message inside of every
javax.servlet.ServletException thrown so that the programmer knows
what has gone wrong with a servlet.
Tomcat uses ResourceBundles to support localized (multilingual)
error messages and store those error messages in a properties file, so editing
them is easy. However, there are hundreds of classes in Tomcat. Storing all
error messages used by all classes in one big properties file will easily
create a maintenance nightmare. To avoid this, Tomcat allocates a properties
file for each package. For example, the properties file for the
org.apache.catalina.connector package contains all error messages
that can be thrown from any class in that package.
Each properties file is handled by an instance of the
org.apache.catalina.util.StringManager class. When Tomcat is run,
there will be many instances of StringManager, each of which reads
a properties file specific to a package. The base name for the
ResourceBundle is LocalStrings, so properties files
must be named "LocalStrings" plus the language code. For example,
the default properties file will be LocalStrings.properties. The
properties file containing messages in the German language is named
LocalStrings_de.properties, for example.
When a class in a package needs to look up an error message in that
package's properties file, it will first obtain an instance of
StringManager. However, many classes in the same package may need
a StringManager, and it is a waste of resources to create a
StringManager instance for every object that needs error messages.
The StringManager class was therefore designed so that an instance
is shared by all objects inside a package. It is a Singleton class with
multiple instances. The StringManager class is too long to list
here, but it's easy to understand.
The only constructor in StringManager is private so that you
cannot use the new keyword to instantiate it from outside the
class. You get an instance by calling its public static method
getManager, passing a package name. Each instance is stored in a
Hashtable with package names as its keys.
private static Hashtable managers = new Hashtable();
public synchronized static StringManager getManager(String packageName) {
StringManager mgr = (StringManager)managers.get(packageName);
if (mgr == null) {
mgr = new StringManager(packageName);
managers.put(packageName, mgr);
}
return mgr;
}
To get an error message, use the StringManager class's
getString method, passing a key. Here is the signature of one of
its overloads:
public String getString(String key)
Pages: 1, 2 |