patternjavaMinor
Utilization of the Multiton pattern
Viewed 0 times
utilizationthepatternmultiton
Problem
See Wikipedia - Multiton Pattern for details on the intent.
Here's the class:
```
/**
* Holds a thread-safe map of unique create-once items.
*
* Contract:
*
* Only one object will be made for each key presented.
*
* Thread safe.
*
* @param
* @param
*/
public class Multiton {
// Map from the key to the futures of the items.
private final ConcurrentMap> multitons = new ConcurrentHashMap>();
// A Prayer is used to construct the Callable that is attached to the FutureTask.
private final Prayer prayer;
public Multiton(Creator creator) {
// Create the prayer to the creator.
this.prayer = new Prayer(creator);
}
/**
* There can be only one.
*
* Use a FutureTask to do the creation to ensure only one construction.
*
* @param key
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
public V getInstance(final K key) throws InterruptedException, ExecutionException {
// Already made?
Future f = multitons.get(key);
if (f == null) {
// Plan the future but do not create as yet.
FutureTask ft = new FutureTask(prayer.pray(key));
// Store it.
f = multitons.putIfAbsent(key, ft);
if (f == null) {
// It was successfully stored - it is the first (and only)
f = ft;
// Make it happen.
ft.run();
}
}
// Wait for it to finish construction and return the constructed.
return f.get();
}
/**
* Use a Prayer to pass the key to the Creator.
*
* @param
* @param
*/
private class Prayer {
private final Creator creator;
public Prayer(Creator creator) {
this.creator = creator;
}
public Callable pray(final K key) {
return new Callable() {
@Override
public V call() throws Exception {
return creator.create(key);
}
};
}
}
/**
* User provides one of these to do the construction.
*
* @param
* @param
*/
pu
Here's the class:
```
/**
* Holds a thread-safe map of unique create-once items.
*
* Contract:
*
* Only one object will be made for each key presented.
*
* Thread safe.
*
* @param
* @param
*/
public class Multiton {
// Map from the key to the futures of the items.
private final ConcurrentMap> multitons = new ConcurrentHashMap>();
// A Prayer is used to construct the Callable that is attached to the FutureTask.
private final Prayer prayer;
public Multiton(Creator creator) {
// Create the prayer to the creator.
this.prayer = new Prayer(creator);
}
/**
* There can be only one.
*
* Use a FutureTask to do the creation to ensure only one construction.
*
* @param key
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
public V getInstance(final K key) throws InterruptedException, ExecutionException {
// Already made?
Future f = multitons.get(key);
if (f == null) {
// Plan the future but do not create as yet.
FutureTask ft = new FutureTask(prayer.pray(key));
// Store it.
f = multitons.putIfAbsent(key, ft);
if (f == null) {
// It was successfully stored - it is the first (and only)
f = ft;
// Make it happen.
ft.run();
}
}
// Wait for it to finish construction and return the constructed.
return f.get();
}
/**
* Use a Prayer to pass the key to the Creator.
*
* @param
* @param
*/
private class Prayer {
private final Creator creator;
public Prayer(Creator creator) {
this.creator = creator;
}
public Callable pray(final K key) {
return new Callable() {
@Override
public V call() throws Exception {
return creator.create(key);
}
};
}
}
/**
* User provides one of these to do the construction.
*
* @param
* @param
*/
pu
Solution
I think your use-case and the Wiki description are different. Technically, the Multiton should return the exact same intance for the exact same key, always, but, because your
This is OK in your case, because you create a
I think, otherwise, the algorithm is good.... (i.e. it will work as advertised).
There are two changes I can recommend....
-
Why is the
-
This is complicated, the rest of the answer is about change 2....
I believe you have unnecessary indirection. Your inner class:
This is overkill.... if you make the Prayer class Extend Callable, you win:
Then, instead of keeping a final instance to
you should instead hold the simple Creator:
And, instead of calling
Note: It is much better to request a
Hope this all makes sense.... I have reworked your original code as:
multitons Map is not a static instance, you can simply create multiple instances of the Multiton and each one will generate different instances for the same key.This is OK in your case, because you create a
private static final anonymous implementation of the class.... so you handle that problem at a higher level.I think, otherwise, the algorithm is good.... (i.e. it will work as advertised).
There are two changes I can recommend....
-
Why is the
Creator a public abstract static class? This is a broken pattern, and it should simply be an interface public interface Creator-
This is complicated, the rest of the answer is about change 2....
I believe you have unnecessary indirection. Your inner class:
private class Prayer {
private final Creator creator;
public Prayer(Creator creator) {
this.creator = creator;
}
public Callable pray(final K key) {
return new Callable() {
@Override
public V call() throws Exception {
return creator.create(key);
}
};
}
}This is overkill.... if you make the Prayer class Extend Callable, you win:
private class Prayer implements Callable {
private final K key;
public Prayer(K key) {
this.key = key;
}
@Override
public V call() throws Exception {
// creator is accessible as a final field on the Multiton instance.
return creator.create(key);
}
}Then, instead of keeping a final instance to
Prayer in your main class:private final Prayer prayer;you should instead hold the simple Creator:
private final Creator creator;And, instead of calling
prayer.pray(), you only need do:FutureTask ft = new FutureTask(new Prayer(key));Note: It is much better to request a
new Prayer(...) than it is to demand one with prayer.pray() !!!Hope this all makes sense.... I have reworked your original code as:
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
public class Multiton {
// Map from the key to the futures of the items.
private final ConcurrentMap> multitons = new ConcurrentHashMap>();
// A Creator is used to construct the Callable that is attached to the FutureTask.
private final Creator creator;
public Multiton(Creator creator) {
this.creator = creator;
}
/**
* There can be only one.
*
* Use a FutureTask to do the creation to ensure only one construction.
*
* @param key
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
public V getInstance(final K key) throws InterruptedException, ExecutionException {
// Already made?
Future f = multitons.get(key);
if (f == null) {
// Plan the future but do not create as yet.
FutureTask ft = new FutureTask(new Prayer(key));
// Store it.
f = multitons.putIfAbsent(key, ft);
if (f == null) {
// It was successfully stored - it is the first (and only)
f = ft;
// Make it happen.
ft.run();
}
}
// Wait for it to finish construction and return the constructed.
return f.get();
}
/**
* Use a Prayer to pass the key to the Creator.
*
* @param
* @param
*/
private class Prayer implements Callable {
private final K key;
public Prayer(K key) {
this.key = key;
}
@Override
public V call() throws Exception {
return creator.create(key);
}
}
/**
* User provides one of these to do the construction.
*
* @param
* @param
*/
public interface Creator {
// Return a new item under the key.
abstract V create(K key) throws ExecutionException;
}
}Code Snippets
private class Prayer {
private final Creator<K, V> creator;
public Prayer(Creator<K, V> creator) {
this.creator = creator;
}
public Callable<V> pray(final K key) {
return new Callable<V>() {
@Override
public V call() throws Exception {
return creator.create(key);
}
};
}
}private class Prayer implements Callable<V> {
private final K key;
public Prayer(K key) {
this.key = key;
}
@Override
public V call() throws Exception {
// creator is accessible as a final field on the Multiton instance.
return creator.create(key);
}
}private final Prayer prayer;private final Creator<K,V> creator;FutureTask<V> ft = new FutureTask<V>(new Prayer(key));Context
StackExchange Code Review Q#39965, answer score: 4
Revisions (0)
No revisions yet.