Introduction
How can we create a class which has the advantages of a Singleton and still supports mock objects and changing behavior?
Background
The Singleton Design Pattern provides developers with a feature where you can guarantee a single instance throughout the life of the application. But, this behavior does not come free and without any problems. The biggest problem is that a Singleton class' behavior cannot be changed in future. Further Mock objects cannot be created, and hence Unit Testing is almost impossible because of bound dependency.
Using the code
Consider the code:
class Singleton
{
private Singleton static instance;
protected Singleton()
{
}
public static Singleton getInstance()
{
if(instance == null)
instance = new Singleton();
return instance;
}
public void show()
{
System.out.println("Bye...");
}
}
Although this class guarantees single instance, it does not allow changing the behavior of its methods like show()
. This may not be apparent, but since the instance returned is always of a Singleton class, the behavior is not changeable!
Going forward, we can do some dependency injection. Now, consider the class MySingleton
:
class MySingleton
{
private static MySingleton instance;
private static MySingletonCreator creator;
public static void setCreator(MySingletonCreator creator)
{
MySingleton.creator = creator;
}
protected MySingleton()
{
}
public void show()
{
System.out.println("Hello...");
}
public static MySingleton getInstance()
{
if(instance == null)
{
if(creator == null)
instance = new MySingleton();
else
instance = creator.create();
}
return instance;
}
public static interface MySingletonCreator
{
public MySingleton create();
}
}
As you can see, the creation of an instance is done by the creator.create()
method call.
The creator can be set right before any instance is demanded, thus allowing the developer to install a creator, its own creator which returns a specialized or mock Singleton !
Now, here is an example:
public class SingletonDemo {
public static void main(String[] args)
{
MySingleton.setCreator(new MySingletonCreatorImpl());
MySingleton ms = MySingleton.getInstance();
ms.show();
}
static class MySingletonCreatorImpl implements MySingletonCreator
{
@Override
public MySingleton create()
{
return new MockSingleton();
}
class MockSingleton extends MySingleton
{
@Override
public void show()
{
System.out.println("Bye...");
}
}
}
}
Here, as we can see, we are setting the creator which is our specialized creator which further returns a mock Singleton. This MockSingleton
has the behavior we want.
Unit Testing this Singleton class is very easy as we just need to install our creator by calling setCreator()
before an instance is created. We can have our own MockSingleton
with any overridden method.
Points of Interest
Singletons are a boon, but they make life miserable when it comes to a unit test or mock. This way, we can get some control over the Singletons.
History
n/a