Injectable Singleton

From APIDesign

(Difference between revisions)
Jump to: navigation, search
(What this is not)
(What this is not)
Line 6: Line 6:
However when talking about [[singleton]] pattern, don't imagine [[Java]]'s '''SecurityManager''' or '''URLStreamHandlerFactory'''. Those are not [[Injectable Singleton]]s, they require non-trivial amount of initialization code and basically let your application knowledge leak out through out the system.
However when talking about [[singleton]] pattern, don't imagine [[Java]]'s '''SecurityManager''' or '''URLStreamHandlerFactory'''. Those are not [[Injectable Singleton]]s, they require non-trivial amount of initialization code and basically let your application knowledge leak out through out the system.
 +
 +
== Defining the [[Singleton]] ==
 +
 +
Imagine you want to create a [[singleton]] service to display questions to the user. This would be your [[API]]:
 +
 +
<source lang="java" snippet="singletons.injectable.api"/>
 +
 +
Slightly surprising thing is that the class is abstract (e.g. it contains no implementation) and to allow extensibility it provides protected constructor. This is not what traditional [[singleton]]s usually do, but remember this is an [[Injectable Singleton]]!
 +
 +
As the important motto of [[Injectable Singleton]]s is [[Convention over Configuration]] each such [[singleton]] needs to define default (even dummy) implementation. Let's do it like this:
 +
 +
<source lang="java" snippet="singletons.injectable.dummyimpl"/>
 +
 +
In this case, the implementation of the '''yesOrNo''' method is really trivial and not very useful, but often there are cases when the default behavior can be made acceptable for many purposes.
 +
 +
Now the time as come to write proper initialization code (that will support [[Component Injection|extensibility]]). We can do it either in [[Java]]6 standard way via [[ServiceLoader]]:
 +
 +
<source lang="java" snippet="singletons.injectable.serviceloader"/>
 +
 +
Or using small [[Lookup]] library (we'll see later what benefit it provides):
 +
 +
<source lang="java" snippet="singletons.injectable.lookup"/>
 +
 +
And that is all. Our first, [[Injectable Singleton]] is ready for being used.
== Configure ==
== Configure ==

Revision as of 19:45, 27 January 2010

Singletons are sometimes dishonest as a design anti-pattern. Right, one may use them improperly, but with guidance of proper methodology there is nothing wrong on singletons. This page provides step by step cook book for usage of so called Injected Singletons.

Contents

What this is not

We are going to introduce an enhanced version of the singleton pattern that supports testability, provides smooth Convention over Configuration and Component Injection. It can be seen as nice alternative companion to application context used by dependency injection.

However when talking about singleton pattern, don't imagine Java's SecurityManager or URLStreamHandlerFactory. Those are not Injectable Singletons, they require non-trivial amount of initialization code and basically let your application knowledge leak out through out the system.

Defining the Singleton

Imagine you want to create a singleton service to display questions to the user. This would be your API:

Code from DialogDisplayer.java:
See the whole file.

public abstract class DialogDisplayer {
    protected DialogDisplayer() {
    }
 
    /** Ask user a question.
     *
     * @param query the text of the question
     * @return true if user confirmed or false if declined
     */
    public abstract boolean yesOrNo(String query);
 
    public static DialogDisplayer getDefault() {
        return Impl.DEFAULT;
    }
}
 

Slightly surprising thing is that the class is abstract (e.g. it contains no implementation) and to allow extensibility it provides protected constructor. This is not what traditional singletons usually do, but remember this is an Injectable Singleton!

As the important motto of Injectable Singletons is Convention over Configuration each such singleton needs to define default (even dummy) implementation. Let's do it like this:

Code from DialogDisplayer.java:
See the whole file.

private static final class Impl extends DialogDisplayer {
    private static final DialogDisplayer DEFAULT = initialize();
 
    @Override
    public boolean yesOrNo(String query) {
        System.err.printf("Saying no to '%s'\n", query);
        return false;
    }
}
 

In this case, the implementation of the yesOrNo method is really trivial and not very useful, but often there are cases when the default behavior can be made acceptable for many purposes.

Now the time as come to write proper initialization code (that will support extensibility). We can do it either in Java6 standard way via ServiceLoader:

Code from DialogDisplayer.java:
See the whole file.

private static DialogDisplayer initializeServiceLoader() {
    Iterator<DialogDisplayer> it = null;
    it = ServiceLoader.load(DialogDisplayer.class).iterator();
    return it != null && it.hasNext() ? it.next() : new Impl();
}
 

Or using small Lookup library (we'll see later what benefit it provides):

Code from DialogDisplayer.java:
See the whole file.

private static DialogDisplayer initializeLookup() {
    final Lookup lkp = Lookup.getDefault();
    DialogDisplayer def = lkp.lookup(DialogDisplayer.class);
    return def != null ? def : new Impl();
}
 

And that is all. Our first, Injectable Singleton is ready for being used.

Configure

TBD. Drop-in your implementations to classpath.

Testability

TBD. Use MockServices

  • mention testing limitations due to levels of Co-existence and solutions

Injectable Meta-Singleton

  • Lookup is meta!
Personal tools
buy