Sometimes it's appropriate to have exactly one instance of a class: window managers, print spoolers, and filesystems are prototypical examples. Typically, those types of objects—known as singletons—are accessed by disparate objects throughout a software system, and therefore require a global point of access. Of course, just when you're certain you will never need more than one instance, it's a good bet you'll change your mind.
The Singleton design pattern addresses all of the previous paragraph's concerns. With the Singleton design pattern you can:
- Ensure that only one instance of a class is created
- Provide a global point of access to the object
- Allow multiple instances in the future without affecting a singleton class's clients
Although the Singleton design pattern—as evidenced below by the figure below—is one of the simplest design patterns, it presents a number of pitfalls for the unwary Java developer. This article discusses the Singleton design pattern and addresses those pitfalls
Ensure a class has only one instance, and provide a global point of access to it.
The figure below illustrates the Singleton design pattern class diagram.
Singleton class diagram
As you can see from the figure above, there's not a whole lot to the Singleton design pattern. Singletons maintain a static reference to the sole singleton instance and return a reference to that instance from a static
instance()
method.Example 1 shows a classic Singleton design pattern implementation:
Example 1. The classic singleton
public class ClassicSingleton { private static ClassicSingleton instance = null; protected ClassicSingleton() { // Exists only to defeat instantiation. } public static ClassicSingleton getInstance() { if(instance == null) { instance = new ClassicSingleton(); } return instance; } }
The singleton implemented in Example 1 is easy to understand. The
ClassicSingleton
class maintains a static reference to the lone singleton instance and returns that reference from the static getInstance()
method.There are several interesting points concerning the
ClassicSingleton
class. First, ClassicSingleton
employs a technique known as lazy instantiation to create the singleton; as a result, the singleton instance is not created until the getInstance()
method is called for the first time. This technique ensures that singleton instances are created only when needed.Second, notice that
ClassicSingleton
implements a protected constructor so clients cannot instantiate ClassicSingleton
instances; however, you may be surprised to discover that the following code is perfectly legal:public class SingletonInstantiator { public SingletonInstantiator() { ClassicSingleton instance = ClassicSingleton.getInstance(); ClassicSingleton anotherInstance = new ClassicSingleton(); ... } }
How can the class in the preceding code fragment—which does not extend
ClassicSingleton
—create a ClassicSingleton
instance if the ClassicSingleton
constructor is protected? The answer is that protected constructors can be called by subclasses and by other classes in the same package. Because ClassicSingleton
and SingletonInstantiator
are in the same package (the default package), SingletonInstantiator()
methods can create ClassicSingleton
instances. This dilemma has two solutions: You can make the ClassicSingleton
constructor private so that only ClassicSingleton()
methods call it; however, that means ClassicSingleton
cannot be subclassed. Sometimes, that is a desirable solution; if so, it's a good idea to declare your singleton class final
, which makes that intention explicit and allows the compiler to apply performance optimizations. The other solution is to put your singleton class in an explicit package, so classes in other packages (including the default package) cannot instantiate singleton instances.Sources :
Singleton.java
import java.util.HashMap;
import org.apache.log4j.Logger;
public class Singleton {
private static HashMap map = new HashMap();
private static Logger logger = Logger.getRootLogger();
protected Singleton() {
// Exists only to thwart instantiation
}
public static Singleton getInstance(String classname) {
Singleton singleton = (Singleton)map.get(classname);
if(singleton != null) {
logger.info("got singleton from map: " + singleton);
return singleton;
}
try {
Class klass = getClass(classname);
singleton = (Singleton)klass.newInstance();
}
catch(ClassNotFoundException cnf) {
logger.fatal("Couldn't find class " + classname);
}
catch(InstantiationException ie) {
logger.fatal("Couldn't instantiate an object of type " + classname);
}
catch(IllegalAccessException ia) {
logger.fatal("Couldn't access class " + classname);
}
map.put(classname, singleton);
logger.info("created singleton: " + singleton);
return singleton;
}
private static Class getClass(String classname)
throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
}
SingletonSubclassOne.java
public class SingletonSubclassOne extends Singleton {
public String toString() {
return "Singleton subclass 1";
}
}
SingletonSubclassTwo.java
public class SingletonSubclassTwo extends Singleton {
public String toString() {
return "Singleton subclass 2";
}
}
No comments:
Post a Comment