web-dev-qa-db-fra.com

Utiliser Enum pour Factory en Java, une meilleure pratique?

Java nous permet d'intégrer des données et des comportements sur Enum. Je ne veux pas implémenter une usine directement sur un Enum, car je pense que ce n'est pas son rôle.

Mais je peux mettre une référence de classe sur l'énumération et construire un objet sur une usine externe. Par rapport à un modèle d'usine traditionnel, quelle est la meilleure mise en œuvre pour vous? Quelle solution est préférable d'utiliser dans quel cas?

Maintenant, le code.

Fonction utilisée dans les deux solutions pour construire des objets. Utile pour implémenter un modèle de poids à la mouche avec une carte si nécessaire.

private Action getAction(Class<? extends Action> actionClazz) {
    // logger + error handling
    return actionClazz.newInstance();
}

1) Avec une usine traditionnelle:

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) Avec l'usine de style Enum:

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

Le second est beaucoup plus propre: il n'a pas besoin de bloc de commutation long et a 0 risque d'oublier l'une des valeurs d'énumération comme la première.

Cependant, il n'est pas toujours possible de l'utiliser, car l'énumération peut être une énumération générique (Month, par exemple), qui ne doit pas être couplée à la fabrique d'actions.

18
JB Nizet

IMO appelant newInstance() doit être évité si possible, car il défait manifestement une partie de la protection de temps de compilation donnée par Java (lire son javadoc) et introduit de nouveaux Exceptions à gérer.

Voici une solution similaire à ce que Sergey a fourni , juste un peu plus concise grâce aux interfaces fonctionnelles et aux références de méthode.

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

Cela fonctionne pour moi:

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

Pour découpler encore plus:

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 est très rapide donc pas de soucis.

7
ZhongYu