Reputation: 653
Java allow us to embed data and behaviour on Enum. I don't want to implement a factory directly on an Enum, because I think this is not its role.
But I can put class reference on the enum, and contruct object on an external factory. Comparing to a traditionnal factory pattern, what is the best implementation for you ? Which solution is better to use in which case ?
Now, the code.
Function used in both solutions to construct objects. Usefull to implement fly-weight pattern with a Map if required.
private Action getAction(Class<? extends Action> actionClazz) {
// logger + error handling
return actionClazz.newInstance();
}
1) With a traditionnal factory:
public enum ActionEnum {
LOAD_DATA,
LOAD_CONFIG;
}
public Action getAction(ActionEnum action) {
switch (action) {
case LOAD_CONFIG:
return getAction(ActionLoadConfig.class);
case LOAD_DATA:
return getAction(ActionLoadData.class);
}
}
2) With Enum-styled factory :
public enum ActionEnum {
LOAD_DATA(ActionLoadConfig.class),
LOAD_CONFIG(ActionLoadData.class);
public ActionEnum(Class<? extends Action> clazz){...}
public Class<? extends Action> getClazz() {return this.clazz}
}
public Action getAction(ActionEnum action) {
return getAction(action.getClazz());
}
Upvotes: 30
Views: 44681
Reputation: 492
IMO calling newInstance()
should be avoided if at all possible, as it blatantly defeats some of the compile time protection given by java (read its javadoc) and introduces new Exception
s to handle.
Here's a solution similar to what Sergey provided, just a little more concise thanks to functional interfaces and method references.
public enum ActionEnum {
LOAD_DATA(ActionLoadData::new),
LOAD_CONFIG(ActionLoadConfig::new)
private Supplier<Action> instantiator;
public Action getInstance() {
return instantiator.get();
}
ActionEnum(Supplier<Action> instantiator) {
this.instantiator = instantiator;
}
}
public Action getAction(ActionEnum action) {
return action.getInstance();
}
Upvotes: 20
Reputation: 2497
This works for me:
enum ActionEnum
{
LOAD_DATA {
@Override
public ActionLoadData getInstance() {
return new ActionLoadData ();
}
},
LOAD_CONFIG {
@Override
public ActionLoadConfig getInstance() {
return new ActionLoadConfig();
}
};
public abstract ILightBulb getInstance();
}
class ActionFactory
{
public Action getAction(ActionEnum action)
{
return action.getInstance();
}
}
Upvotes: 22
Reputation: 19682
To decouple even more:
static final EnumMap<ActionEnum, Class<? extends Action>> enumToClass = new EnumMap<>();
static
{
enumToClass.put(ActionEnum.LOAD_DATA, ActionLoadData.class);
etc...
}
public Action getAction(ActionEnum action)
{
return getAction(enumToClass.get(action));
}
EnumMap
is very fast so no worries.
Upvotes: 8
Reputation: 692073
The second one is much cleaner: it doesn't need any long switch block, and has 0 risk of forgetting one of the enum values like the first one has.
It's not always possible to use it, though, because the enum might be some generic enum (Month
, for example), that should not be coupled to the factory of actions.
Upvotes: 20