如何实现近单身?

时间:2010-12-23 18:16:18

标签: java unit-testing design-patterns singleton

我有时需要只在应用程序生命周期中实例化一次的类。让他们成为单身人士是不好的,因为单元测试变得有问题。

但是,因为在我的应用程序的生命周期中应该有这样的对象的唯一实例,所以在应用程序运行时两次实例化这样的对象将是错误的。< / p>

因此,我希望我的应用程序在其生命周期中检测到这样的对象实例化两次同时仍然允许在单元测试时对这样的对象进行多次实例化时抛出异常

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

这就是我正在做的事情:

/**
 * 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()在我进行单元测试时应始终返回 false ,并且在真实应用运行时始终 true

我的问题很简单:它是否有意义,我应该如何实现?

3 个答案:

答案 0 :(得分:9)

如果你的设计需要单身,那么你最好使用单身而不是尝试复杂的事情。

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

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

然后,您可以在测试中使用它,并使代码库保持单例不变。轻量级代理仍然是测试套件的一部分,而不是发布。

答案 1 :(得分:5)

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

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

有关详细信息:http://static.springsource.org/spring/docs/2.5.x/reference/testing.html

另一种方法是,如果您已经在代码中添加了静态单例引用,则将Singleton.getInstance()方法转换为真正的工厂,使用系统属性来控制其行为。此系统属性可以像标志一样简单,以指示测试正在进行中,或者与用作实际工厂的另一个类的名称一样复杂(类似于JDK对DocumentBuilderFactory的处理)。

答案 2 :(得分:0)

  

因此,我希望我的应用程序在其生命周期中检测到此类对象实例化两次后立即抛出异常

这就是辛格尔顿所执行的。

  

虽然仍然允许在单元测试时对这样的对象进行多次实例化。

您可以使用@com.dp4j.Singleton

强制执行此操作

这就是Reflection API允许的内容。

您可以直接使用Reflection API,也可以让dp4j为您注入。 Here你会找到两者的代码。