代码重用:使用常用 getter 方法返回枚举字段列表

Code reuse: returning lists of enum fields with common getter methods

我有两个枚举:

主菜单选项

public enum MainMenuOptions {
    
    EXIT("Exit"),
    VIEW_RESERVATIONS("View Reservations By Host"),
    CREATE_RESERVATION("Create A Reservation"),
    EDIT_RESERVATION("Edit A Reservation"),
    CANCEL_RESERVATION("Cancel A Reservation");
    
    private final String message;
    
    MainMenuOptions(String message) {
        this.message = message;
    }
    
    public String getMessage() {
        return message;
    }
    
    public static List<String> asListString() {
        return Arrays.stream(MainMenuOptions.values())
                .map(MainMenuOptions::getMessage)
                .collect(Collectors.toList());
    }
}

主机选择方法选项

public enum HostSelectionMethodOptions {
    
    FIND_ALL("Find all"),
    FIND_BY_LASTNAME_PREFIX("Find by last name prefix"),
    FIND_BY_CITY_STATE("Find by city & state");
    
    String message;
    
    HostSelectionMethod(String message) {
        this.message = message;
    }
    
    public String getMessage() {
        return message;
    }
    
    public static List<String> asListString() {
        return Arrays.stream(HostSelectionMethod.values())
                .map(HostSelectionMethod::getMessage)
                .collect(Collectors.toList());
    }
}

两个枚举共享同一个字段

private final String message;

一样getter

public String getMessage() {
    return message;
}

与ListString()方法相同

public static List<String> asListString() {
    return Arrays.stream(MainMenuOptions.values())
            .map(MainMenuOptions::getMessage)
            .collect(Collectors.toList());
}

我怎样才能干掉这些枚举?

我希望有更多具有相同字段和方法的枚举,并且为每个枚举一遍又一遍地写出相同的东西似乎很愚蠢。

我希望代码的风格是这样的:

public class Utils {
    
    public static List<String> enumAsListString(Enum e) {
        return e.values().stream.map(e::getMessage).collect(Collectors.toList());
    }
}

这可能是您需要在 DRY 和使用枚举之间做出选择的情况之一。

就代码重用而言,枚举并没有走得太远,至少在 Java 中是这样;这样做的主要原因是使用枚举的主要好处是在静态代码中获得——我的意思是 static 作为“非动态”/“运行时”,而不是 static :).尽管您可以“减少”代码重复,但如果不引入依赖性(是的,这适用于添加通用 API/interface,将 asListString 的实现提取到实用程序 class).这仍然是一个不受欢迎的权衡。

此外,如果您必须使用枚举(出于对序列化、数据库映射、JSON绑定的内置支持等原因,或者,嗯,因为它是数据枚举等),你别无选择,只能在一定程度上重复方法声明,即使你可以共享实现:静态方法不能被继承,接口方法(其中 getMessage 会是一个)应该到处都需要实施。我的意思是这种“干燥”的方式会有很多不优雅的方式。

如果我是你,我会让这个数据完全动态化

final class MenuOption {
    private final String category; //MAIN_MENU, HOT_SELECTION
    private final String message; //Exit, View Reservation By Host, etc.
    public static MenuOption of(String key, String message) {
        return new MenuOption(key, message);
    }
}

这是非常可扩展的,尽管它引入了验证数据的需要,其中枚举将静态地防止错误的选项,并且可能需要自定义代码,其中枚举将提供内置支持。

它可以通过“类别”枚举进行改进,它提供对菜单列表的静态访问,以及 asListString():

的单一位置
enum MenuCategory {
    MAIN_MENU(
        MenuOption.of("Exit"), 
        MenuOption.of("View Reservations By Host")
    ),
    HOT_SELECTION(
        MenuOption.of("Find All")
    );
    
    private final List<MenuOption> menuOptions;
    
    MenuCategory(MenuOption... options) {
        this.menuOptions = List.of(options); //unmodifiable
    }
    
    public List<String>asListString() {
        return this.menuOptions.stream()
                   .map(MenuOption::getMessage)
                   .collect(Collectors.toList());
    }
}

应该清楚的是,您可以用一堆实现通用接口的枚举替换 class MenuOption,这在 MenuCategory 中应该几乎没有变化。我不会那样做,但这是一个选择。

你可以稍微晾干一下。

Utils.java

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public interface Utils<T>
{

   public String getMessage();

   public static <T extends Utils<T>> List<String> asListString(Class<T> clazz)
   {
   
      return Arrays.stream(clazz.getEnumConstants())
                .map(T::getMessage)
                .collect(Collectors.toList());
   
   }
   
}

HostSelectionMethodOptions.java

public enum HostSelectionMethodOptions implements Utils<HostSelectionMethodOptions> {
   
   FIND_ALL("Find all"),
   FIND_BY_LASTNAME_PREFIX("Find by last name prefix"),
   FIND_BY_CITY_STATE("Find by city & state");
   
   private final String message;
   
   HostSelectionMethodOptions(String message) {
      this.message = message;
   }
   
   public String getMessage() {
      return message;
   }
   
}

然后就这样做 - Utils.asListString(HostSelectionMethodOptions.class);

我和davidalayachew的想法基本一样

一个枚举可以实现一个接口。因此,如果您创建一个接受 enum 类型的通用 asListString,您可以获得所需的结果。

首先,创建一个 Options 接口并让两个 enum 实现它:

interface Options {
    String getMessage();
}
enum HostSelectionMethodOptions implements Options { ... }
enum MainMenuOptions implements Options { ... }

现在创建一个这样的方法:

public static <T extends Enum<T> & Options> List<String> asListString(Class<T> type) {
    return Arrays.stream(type.getEnumConstants())
        .map(T::getMessage)
        .collect(Collectors.toList());
}

该方法声明了一个类型参数:<T extends Enum<T> & Options>。这里,T 是一个 交集类型 ,因此它扩展了 EnumOptions 接口。你可以这样称呼它:

asListString(MainMenuOptions.class);