mechanicalSPIRIT

Brandon Franklin's blog

« Back to blog

Java Programming Tip: Keyed Singletons

Most Java programmers are familiar with the "singleton" pattern, shown in this snippet:

public class MyService {

    static private MyService INSTANCE = null;

    static public MyService getInstance() {
        if( INSTANCE == null ) {
            INSTANCE = new MyService();
        }

        return INSTANCE;
    }

    private MyService() {
    }

}

However, I've found that sometimes in a complex application, it can be useful to have instances of particular classes that act like singletons, but only in terms of a specific context. For example, perhaps you are working on an application that allows you to edit "projects" of some sort, and within the scope of each project you only want exactly one instance of a particular service, such as an "Undo Manager" or a "History Manager". You want it to work like a utility class, but it needs to maintain state that is specific to the context in which it's being used.

My solution to this problem has been to use what I call a "keyed singleton" pattern. It's easy to implement, as shown here (NOTE: uses Java 5 language syntax):

public class MyService {

    static private final Map<Object,MyService> INSTANCES = new HashMap<Object,MyService>();

    static public MyService getInstance(Object key) {
        MyService service = null;

        if( INSTANCES.get(key) == null ) {
            service = new MyService();
            INSTANCES.put(key, service);
        }

        return service;
    }

    private MyService() {
    }

}

In the above example, instances are lazily initialized just like you'd expect from a singleton implementation, but then they're added to a Map which tracks every instance based on the key it was created with. One will quickly notice that there's a problem with using this approach in some cases, though, which is that there's no obvious removal semantic. In other words, you'll quickly end up with a lot of instances in your Map and no way to get rid of them. Also, if you are using large objects (such as some kind of Project class) as your keys, then you could be creating a rather severe memory leak. You could add a removal method, but I have a better way!

Just substitute in this line:

    static private final Map<Object,MyService> INSTANCES = new WeakHashMap<Object,MyService>();

Instantly you have now made all of the references to your keys into "weak references," meaning the Map itself cannot prevent garbage collection of the key instances. Better yet, when one of the keys is garbage collected, its associated value entry is removed from the Map as well. In this way, careful use of a keyed singleton pattern can provide a scoped, self-cleaning service management framework.

Loading mentions Retweet
 
To leave a comment on this posterous, please login by clicking one of the following.
Posterous-login     twitter