如何创建行为可被 Mock 对象覆盖的单例
如何创建既具有 Singleton 的优点,又支持 Mock 对象和行为改变的类。
引言
我们如何创建一个类,它既具有 Singleton 的优势,又支持 Mock 对象和行为改变?
背景
Singleton 设计模式为开发者提供了一种特性,可以保证在应用程序的整个生命周期内只有一个实例。但是,这种行为并非免费的,并且存在一些问题。最大的问题是 Singleton 类的行为在未来无法改变。此外,无法创建 Mock 对象,因此由于绑定依赖关系,几乎不可能进行单元测试。
使用代码
考虑以下代码
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...");
}
}
虽然这个类保证了单例实例,但它不允许改变其方法(如 show()
)的行为。这可能不明显,但由于返回的实例始终是 Singleton 类,因此行为是不可变的!
接下来,我们可以进行一些依赖注入。现在,考虑类 MySingleton
class MySingleton
{
private static MySingleton instance;
/**
* Singleton creator
*/
private static MySingletonCreator creator;
public static void setCreator(MySingletonCreator creator)
{
MySingleton.creator = creator;
}
/**
* Only subclass can use
*/
protected MySingleton()
{
}
// This is the behavior which developer wants to change !
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();
}
}
如你所见,实例的创建是通过调用 creator.create()
方法完成的。
可以在任何实例被要求之前设置 creator,从而允许开发者安装一个 creator,甚至是返回专门的或 Mock Singleton 的 creator!
现在,这里有一个例子
public class SingletonDemo {
public static void main(String[] args)
{
// Set the creator
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...");
}
}
}
}
这里,我们可以看到,我们正在设置 creator,它就是我们的专门 creator,进一步返回一个 Mock Singleton。这个 MockSingleton
具有我们想要的行为。
对这个 Singleton 类进行单元测试非常容易,我们只需要在创建实例之前通过调用 setCreator()
安装我们的 creator。我们可以拥有自己的 MockSingleton
,并覆盖任何方法。
关注点
Singleton 是一种福音,但当涉及到单元测试或 Mock 时,它们会使生活变得痛苦。通过这种方式,我们可以对 Singleton 获得一些控制权。
历史
不适用