Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement a near-singleton?

I sometimes have a need for classes that should only be instantiated once during the lifecycle of the application. Making them singletons is bad because then unit testing becomes problematic.

But still, because there should be one and only instance of such objects during the lifecycle of my application, it would be an error to ever instantiate such an object twice when the application is running.

Hence I'd like my app to throw an exception as soon as it detects, during its lifecycle, that such an object is instantiated twice while still allowing several instantiation of such an object while unit testing.

I think it's not an unreasonable requirement: if during one lifecycle of the application only one such object should be created then throwing an exception seems like a correct thing to do.

Here's what I'm doing:

/**
 * 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();
    }

}

where App.isRealApp() shall always return false when I'm unit testing and always true when the real app is running.

My question is simple: does it makes sense and how am I supposed to implement this?

like image 449
NoozNooz42 Avatar asked Dec 23 '10 18:12

NoozNooz42


1 Answers

If your design requires a singleton, then you're best off with a singleton rather than trying something complicated.

If your trouble in unit testing singletons is getting an instance to mock, for example, then I'd create an instantiable lightweight proxy that provides the same interface as your singleton.

The proxy should have no logic -- it should just map calls through to the singleton.

You can then use it in tests, and leave the codebase with singleton intact. The lightweight proxy can remain part of the testing suite, and not be released.

like image 137
Tim Barrass Avatar answered Oct 10 '22 08:10

Tim Barrass