'Map binding of generic factories
Suppose we have a couple of Shape implementations. Each implementation is created by a ShapeFactory from a corresponding ShapeConfig.
interface Shape {}
class Circle implements Shape {}
class Square implements Shape {}
...
-----
abstract class ShapeConfig {
abstract String getName();
}
class CircleConfig extends ShapeConfig {}
class SquareConfig extends ShapeConfig {}
...
-----
interface ShapeFactory<C extends ShapeConfig> {
Shape create(C config);
}
class CircleConfig implements ShapeFactory<CircleConfig> {}
class SquareConfig implements ShapeFactory<SquareConfig> {}
...
Given a set of ShapeConfig objects, I want to construct a map binding of Shape objects by their configured name:
class ShapeModule extends AbstractModule {
@Override
protected void configure() {
ShapeFactory<CircleConfig> circleFactory = new CircleFactory();
ShapeFactory<SquareConfig> squareFactory = new SquareFactory();
// ...
MapBinder<String, Shape> shapesByName = newMapBinder(binder(), String.class, Shape.class);
for (ShapeConfig config : getShapeConfigs()) {
if (config instanceof CircleConfig) {
Shape shape = circleFactory.create((CircleConfig) config);
shapesByName.addBinding(config.getName()).toInstance(shape);
continue;
}
if (config instanceof SquareConfig) {
Shape shape = squareFactory.create((SquareConfig) config);
shapesByName.addBinding(config.getName()).toInstance(shape);
continue;
}
// ...
}
}
}
In the above example, the map binder is pretty much useless but I hope it clarifies my intent. What would a proper solution look like?
Solution 1:[1]
In your example it looks like you:
- Have static Shape Configs (at load time)
- Are attempting to use
Shape circle = shapes.get("circle");returning effectively a singletonCircle.
Which if you want, seems pretty spot on. But if you wanted to dynamically generate Shape(s) based on runtime ShapeConfig(s) then you might want to do something like:
MapBinder<ShapeConfig, ShapeFactory> shapeFactories = MapBinder.newMapBinder(binder(), ShapeConfig.class, ShapeFactory.class);
shapeFactories.addBinding(CircleConfig.class, CircleFactory.class);
shapeFactories.addBinding(SquareConfig.class, SquareFactory.class);
// etc.
Which would then be used as:
public class ShapeManager {
@Inject Map<ShapeConfig, ShapeFactory> factories;
public <T implements ShapeConfig, R implements Shape> R manage(T config) {
Shape shape = factories.get(config.class).create(config);
// do stuff?
return shape;
}
}
which you can then call how you like:
Shape shape = manager.manage(new CircleConfig(...));
// shape should be a Circle
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | kendavidson |
