简体   繁体   中英

Using Enum for factory in Java, a best practice?

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());
}

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.

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();
}

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();
    }
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM