2010-12-23 50 views
5

我有时需要在应用程序的生命周期中只应该实例化一次的类。让他们单身是不好的,因为然后单元测试就成了问题。如何实现一个近单身?

但是,由于在我的应用程序的生命周期中应该只有一个这样的对象的实例,因此在应用程序运行时将这样的对象两次实例化为将是错误

因此,我想我的应用程序尽快检测抛出一个异常,它的生命周期中,这样的一个对象实例化两次同时仍然允许这种对象的几个实例,而单元测试

我认为这不是一个不合理的要求:如果在应用程序的一个生命周期中只有一个这样的对象应该被创建,那么抛出异常似乎是一个正确的事情。

下面是我在做什么:

/** 
* The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", 
* "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this 
* document are to be interpreted as described in RFC 2119. 
* 
* You MUST NOT instantiate this class more than once during the application's 
* lifecycle. If you try to do so, an exception SHALL be thrown. 
* 
* You SHOULD be able to instantiate this class more than once when unit 
* testing. 
* 
* 
*/ 
public class LifeCycle { 

    private static final AtomicInteger cnt = new AtomicInteger(0); 

    @NotNull 
    public static LifeCycle getNewLifeCycle() { 
     if (cnt.incrementAndGet() > 1 && App.isRealApp()) { 
      throw new IllegalStateException("Class is already instantiated"); 
     } 
     return new LifeCycle(); 
    } 

} 

其中App.isRealApp()应始终返回当我的单元测试,并始终真正当真正的应用程序正在运行。

我的问题很简单:它有道理,我该如何实现?

回答

9

如果你的设计需要一个singleton,那么你最好使用singleton而不是尝试一些复杂的东西。

如果你在单元测试单例中遇到的麻烦是得到一个实例来模拟,例如,然后我会创建一个可实例化的轻量级代理,它提供与你的单例相同的接口。

代理应该没有逻辑 - 它应该只是将调用映射到单例。

然后,您可以在测试中使用它,并保持单身完整的代码库。轻量级代理可以保留为测试套件的一部分,不会被释放。

+1

+1,我喜欢轻量级的代理理念! – NoozNooz42 2010-12-23 18:36:14

+0

这对我很有用 - 意味着我可以保持代码基本不变。 – 2010-12-23 18:58:19

5

一个解决方案是使用像Spring这样的依赖注入框架。

应用程序配置将指定单例实例,但您的测试用例仍可以创建按需实例并手动注入它们。

欲了解更多信息:http://static.springsource.org/spring/docs/2.5.x/reference/testing.html

的选择,如果你已经有了整个代码洒静态单的引用,是您Singleton.getInstance()方法转换成一个真正的工厂,使用系统属性来控制其行为。这个系统属性可以像标志一样简单,以表明测试正在进行中,或者与用作真实工厂的另一个类的名称一样复杂(类似于JDK用DocumentBuilderFactory所做的)。

0

因此,我想我的应用程序尽快检测抛出一个异常,它的生命周期中,这样的一个对象实例化两次

这就是辛格尔顿强制执行的。

虽然仍然允许几个实例化这样的对象,而单元测试。

你可以用@com.dp4j.Singleton

这就是反射API允许执行此操作。

您可以直接使用Reflection API或让dp4j为您注入。 Here你会发现两者的代码。