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());
}
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.
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 Exception
s à 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();
}
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();
}
}
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.