One possibility would be using an enum. On the simplest level, you could replace constants like Rose.NAME
with enum values, and maintain an internal mapping between enum values and classes to instantiate:
public enum Flowers {
ROSE(Rose.class),
OLEANDER(Oleander.class);
private final Class<? extends Flower> flowerClass;
Flowers(Class<? extends Flower> flowerClass) {
this.flowerClass = flowerClass;
}
public Flower getFlower() {
Flower flower = null;
try {
flower = flowerClass.newInstance();
} catch (InstantiationException e) {
// This should not happen
assert false;
} catch (IllegalAccessException e) {
// This should not happen
assert false;
}
return flower;
}
}
Since the flower classes classes have no default constructor, Class.newInstance()
can not be used, so instantiating the class via reflection is a bit more cumbersome (although possible). An alternative could be to use a Prototype to create the new flower instance.
This already ensures that you always keep the mapping between possible flower names and actual flower classes in sync. When you add a new flower class, you must create a new enum value, which includes the mapping to create new class instances. However, the problem with the enum aproach is that the Garden
instance you use is fixed at startup. (Unless you pass it as a parameter to getFlower()
- but then there is a risk of losing coherence, i.e. it is harder to ensure that a specific group of flowers is created in a specific garden).
If you want to be even more flexible, you may consider using Spring to move the whole mapping between names and concrete (bean) classes out to a configuration file. Your factory then simply loads a Spring ApplicationContext in the background and uses the mapping defined in it. Whenever you introduce a new flower subclass, you just need to add a new line to the config file. Again, though, this approach, in its simplest form, requires you to fix the Garden
bean instance at configuration time.
If you want to switch between different gardens at runtime, and ensure consistency between gardens and groups of flowers, a Factory using an internal map of names to flower classes may be the best choice. Whereas the mapping itself can again be stored in configuration, but you can instantiate distinct factory instances with distinct Garden
instances at runtime.