What is a Multiton?
Simply put, a multiton is the multiple instance version of the singleton pattern. It is used when there is a class that you want to allow to be instantiated multiple times, but want to limit the total number of instances.
Callers provide a key to the multion to get the instance they want.
As an example, consider a system with two network cards, where the caller gets to determine which card they want their network traffic to use. Both cards use the same network stack, so they need to use the same base class, but they need to have separate instances. An example usage might be:
NetworkMultiton.getInstance("card1").sendData();
Show Me the Code
First, let's review the singleton pattern. The prototypical example is:
class Singleton {
private static Singleton instance;
private Singleton() {
//do setup
}
public static Singleton getInstance() {
if(instance==null) {
instance = new Singleton();
}
return instance;
}
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
Callers are restricted from creating multiple instances by the fact that the constructor is private. The getInstance() method is their only opportuntiy to get an instance of the class. One of the downsides of the Singleton pattern is that there is a lot of boilerplate code that must be inserted (highlighted in yellow). A typo in this code, such as forgetting to make the constructor private, leads to a singleton that could be instantiated multiple times.
What if you wanted to have two instances of a singleton (without using the multiton pattern)? One way to accomplish this would be to have an abstract class containing the shared code and then concrete classes, one for each desired instance:
abstract class AbstractSingleton {
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
class ConcreteSingleton1 extends AbstractSingleton {
private static ConcreteSingleton1 instance;
private ConcreteSingleton1() {
//do setup
}
public static ConcreteSingleton1 getInstance() {
if(instance==null) {
instance = new ConcreteSingleton1();
}
return instance;
}
}
class ConcreteSingleton2 extends AbstractSingleton {
private static ConcreteSingleton2 instance;
private ConcreteSingleton2() {
//do setup
}
public static ConcreteSingleton2 getInstance() {
if(instance==null) {
instance = new ConcreteSingleton2();
}
return instance;
}
}
There are several issues with this. First, both concrete classes are nothing but boilerplate code – the risk of a typo has been doubled. Second of all, code cleaniness has suffered – you have three source files (one per class), two of which are nothing more than boilerplate code. The next user of this class will need to examine three files in order to fully understand how this code works. Third, it’s also possible that someone might see the abstract class and not realize that it’s part of a multiple singleton pattern and create a concrete class that extends it, but isn’t a singleton.
Let’s now look at the basic multiton pattern:
public class Multiton {
private static Map instances = new HashMap();
private Multiton() {
//do setup
}
public static Multiton getInstance(String key) {
Multiton instance = instances.get(key);
if(instance==null) {
instance = new Multiton();
instances.put(key, instance);
}
return instance;
}
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
This version doesn’t restrict the total number of instances, but it does gaurantee that if a caller passes the same key, they will get the same instance. We still have the same problem of the class being mostly boilerplate code.
To restrict the number of instances, we change the key to an enum:
class LimitedMultiton {
private static Map instances = new HashMap();
public static enum keys{INSTANCE1, INSTANCE2};
private LimitedMultiton() {
//do setup
}
public static LimitedMultiton getInstance(Enum key) {
LimitedMultiton instance = instances.get(key);
if(instance==null) {
instance = new LimitedMultiton();
instances.put(key, instance);
}
return instance;
}
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
This is getting closer – now callers are restricted to a small set of instances to choose from. We still have the boilerplate code problem and another issue is the clunky way callers must get their instance:
LimitedMultiton.getInstance(LimitedMultiton.keys.INSTANCE1).methodThatDoesSomethingInteresting();
Java 5 re-introduces enums as a special type of class which allows us to greatly cleanup the multiton:
enum LimitedMultitonWithEnum {
INSTANCE1, INSTANCE2;
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
Now, all of the boilerplate code has been replaced with the listing of the constants, which must be the first thing in the enum. The callers allow get the instance in a much easier way:
LimitedMultitonWithEnum.INSTANCE1.methodThatDoesSoemthingInteresting();
Now, a couple of notes on enums. First, they are not allow to extend other classes. Other classes can't extend them. They can implement interfaces. There is a default, private constructor. You can write your own constructor, but it can't be public. Any instance that won't use the default constructor passes it's parameters in the first line:
enum LimitedMultitonWithEnumWithConstructorParameters {
INSTANCE1(true),
INSTANCE2;
private boolean syncOnStart;
private LimitedMultitonWithEnumWithConstructorParameters(boolean syncOnStart) {
this.syncOnStart = syncOnStart;
}
private LimitedMultitonWithEnumWithConstructorParameters() {
this(false);
}
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
It’s also possible to override the default implementation of methods in the multiton for just a single instance:
enum LimitedMultitonWithEnumAndOverride {
INSTANCE1,
INSTANCE2 {
@Override
public void methodThatDoesSomethingInteresting() {
super.methodThatDoesSomethingInteresting();
//do something else interesting
}
};
public void methodThatDoesSomethingInteresting() {
//do something interesting
}
}
Overriding methods is very ugly, but this has a nice side effect. It actual encourages cleaner code – because it’s not easy to override methods, they’ll only be overriden if absolutely necessary. And if you find that you need to override a bunch of methods to make the multiton work, then this code probably really isn’t “several instances of the same code” (and should be refactored into a different pattern).