Guicin’ Up Abstract Factories Like a DI Boss

Jameson Williams
2 min readMar 28, 2017

The MapBinder in Guice provides a convenient way to do modular component registration.

In your module’sconfigure() logic, you can bind some objects to some class definitions, and you now have a module dictionary.

A basic example is:

public class FruitModule extends AbstractModule {
...
@Override
protected void configure() {
fruitBindings = MapBinder.newMapBinder(
binder(),
String.class,
Fruit.class
);

fruitBindings.addBinding("Apple").to(Apple.class);
}
...
}

Elsewhere, you can inject the mapping and use it to get Apple instances by name:

public class FruitBasket {    private final List<Fruit> basket;      private final Map<String, Fruit> nameToFruitMap;    @Inject
public FruitBasket(final Map<String, Fruit> nameToFruitMap) {
this.basket = new ArrayList<>();
this.nameToFruitMap = nameToFruitMap;
}
public void addToBasket(final String fruitName) {
basket.add(nameToFruitMap.get(fruitName));
}
}

This works great, except if you have a ton of objects in the mapping, or your FruitModule is already long and complicated.

Maintaining a Registry

It would be cool, then, if you could just instantiate a regular map, and iterate over it to add bindings instead of writing:

fruitBindings.addBinding("Apple").to(Apple.class);

For each key-value pair.

Similarly, if your module is using multiple MapBinder s, this problem will already be exacerbated.

What we can do is to maintain a registry, in a new FruitRegistry class.

private static final Map<Intent, Class<? extends Fruit>> REGISTERED_FRUITS =
ImmutableMap.of(
"Apple", Apple.class,
"Orange", Orange.class
);

And then provide a method in the registry which adds its registered components to a MapBinder :

public static void bindRegistrations(
final MapBinder<String, Fruit> fruitBindings) {

REGISTERED_FRUITS.forEach((key, value) ->
fruitBindings.addBinding(key).to(value)
);
}

Now, back in your Guice module, all you need to do inflate the registry is to invoke that method on the MapBinder you are creating:

FruitRegistry.bindRegistrations(MapBinder.newMapBinder(
binder(), String.class, Fruit.class
));

Using it as an Abstract Factory

The above is kind of contrived since it only deals with a simple POJO.

However, the approach above becomes increasingly useful as you deal with more complicated and interactive objects. In particular, it provides a great way to do a clean Abstract Factory.

The standard Abstract Factory lands you eventually with some code like:

FruitFactory factory = null;if (fruitInput.equals("apple")) {
factory = AppleFactory();
} else if (fruitInput.equals("orange")) {
factory = OrangeFactory();
} else if (fruitInput.equals("banana")) {
factory = BananaFactory();
}
Fruit fruit = factory.createFruit();

Whenever you see big blocks of code like that, a siren should go off in your head. This is nasty looking stuff.

With the module registry described above, our logic would be added as a key-value pair to the registration map. From there, the selection of a concrete factory is just:

FruitFactory factory = nameToFruitFactoryMap.get(fruitInput);
Fruit fruit = factory.createFruit();

Much cleaner.

Final Thoughts

Above, I still maintain a static registry. I don’t think there’s anything that forces you do this, however. You could dynamically generate the map for the registry class at run-time. Then, you could inject the module lazily, resulting in late binding of the items in the registry.

Have fun!

--

--