使用Enum在Java工厂,最佳实践?

时间:2023-01-26 13:14:35

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.

Java允许我们在Enum上嵌入数据和行为。我不想直接在Enum上实现工厂,因为我认为这不是它的作用。

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.

两种解决方案中用于构造对象的函数。如果需要,可以使用Map实现fly-weight模式。

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

1) With a traditionnal factory:

1)与传统工厂:

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 :

2)使用Enum风格的工厂:

public enum ActionEnum {
    LOAD_DATA(ActionLoadConfig.class),
    LOAD_CONFIG(ActionLoadData.class);

    public ActionEnum(Class<? extends Action> clazz){...}
    public getClazz() {return this.clazz}
}

public Action getAction(ActionEnum action) {
    return getAction(action.getClazz());
}

4 个解决方案

#1


15  

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.

但是,并不总是可以使用它,因为枚举可能是一些通用的枚举(例如,月),它不应该耦合到动作的工厂。

#2


7  

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.

EnumMap非常快,所以不用担心。

#3


7  

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

#4


4  

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 Exceptions to handle.

如果可能的话,应该避免IMO调用newInstance(),因为它公然违反了java给出的一些编译时保护(读取它的javadoc)并引入了新的Exceptions来处理。

Here's a solution similar to what Sergey provided, just a little more concise thanks to functional interfaces and method references.

这是一个类似于Sergey提供的解决方案,由于功能接口和方法引用,更简洁一点。

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

#1


15  

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.

但是,并不总是可以使用它,因为枚举可能是一些通用的枚举(例如,月),它不应该耦合到动作的工厂。

#2


7  

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.

EnumMap非常快,所以不用担心。

#3


7  

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

#4


4  

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 Exceptions to handle.

如果可能的话,应该避免IMO调用newInstance(),因为它公然违反了java给出的一些编译时保护(读取它的javadoc)并引入了新的Exceptions来处理。

Here's a solution similar to what Sergey provided, just a little more concise thanks to functional interfaces and method references.

这是一个类似于Sergey提供的解决方案,由于功能接口和方法引用,更简洁一点。

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