根据定义,单例仅具有一个实例。 因此,其创建受类本身严格控制。 通常,它是一个具体的类,而不是一个接口,并且由于其私有的构造函数,因此它不是子类的。 此外,它可以被其客户积极地找到(致电Singleton.getInstance()或等效帐户),因此您不能轻易地使用例如 依赖注入将其“真实”实例替换为模拟实例:
class Singleton {
private static final myInstance = new Singleton();
public static Singleton getInstance () { return myInstance; }
private Singleton() { ... }
// public methods
}
class Client {
public doSomething() {
Singleton singleton = Singleton.getInstance();
// use the singleton
}
}
对于模拟,理想情况下,您需要一个可以自由子类化的接口,并通过依赖注入将其具体实现提供给其客户端。
您可以放宽Singleton的实现,以使其可测试
提供一个可以由模拟子类以及“真实”子类实现的接口
添加了setInstance方法以允许在单元测试中替换实例
例:
interface Singleton {
private static final myInstance;
public static Singleton getInstance() { return myInstance; }
public static void setInstance(Singleton newInstance) { myInstance = newInstance; }
// public method declarations
}
// Used in production
class RealSingleton implements Singleton {
// public methods
}
// Used in unit tests
class FakeSingleton implements Singleton {
// public methods
}
class ClientTest {
private Singleton testSingleton = new FakeSingleton();
@Test
public void test() {
Singleton.setSingleton(testSingleton);
client.doSomething();
// ...
}
}
如您所见,您只能通过损害Singleton的“清洁度”来使使用Singleton的代码单元可测试。 最后,如果可以避免的话,最好不要使用它。
更新:这是迈克尔·费瑟斯(Michael Feathers)强制性使用“遗留代码有效工作”的参考。