简体   繁体   English

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

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

I have two enums:我有两个枚举:

Main Menu Options主菜单选项

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

Host Selection Method Options主机选择方法选项

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

Both enums share the same field两个枚举共享相同的字段

private final String message;

The same getter同一个吸气剂

public String getMessage() {
    return message;
}

And the same asListString() method和 ListString() 方法一样

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

How can I DRY out these enums?我怎样才能干掉这些枚举?

I expect to have more enums with the same fields and methods, and it seems silly to write out the same thing over and over again for each one.我希望有更多具有相同字段和方法的枚举,并且为每个枚举一遍又一遍地写出相同的东西似乎很愚蠢。

  • I tried making both of the enums extend a superclass, but enums cannot have extends clauses我尝试让两个枚举都扩展一个超类,但枚举不能有扩展子句
  • I can create an interface that specifies the contract for the asListString() method, but that doesn't allow me to actually reuse any code.我可以创建一个接口来指定 asListString() 方法的协定,但这不允许我实际重用任何代码。

The flavor I was hoping the code could have is something like this:我希望代码的味道是这样的:

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

I basically had the same idea as davidalayachew.我基本上和 davidalayachew 有同样的想法。

An enum can implement an interface.一个枚举可以实现一个接口。 So if you create a common asListString which accepts an enum type, you could get the desired result.因此,如果您创建一个接受enum类型的通用asListString ,您可以获得所需的结果。

First, create an Options interface and let both enum s implement it:首先,创建一个Options接口并让两个enum实现它:

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

Now create a method like this:现在创建一个这样的方法:

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

The method declares a type argument: <T extends Enum<T> & Options> .该方法声明了一个类型参数: <T extends Enum<T> & Options> Here, T is an intersection type , so it extends both Enum and the Options interface.这里, T是一个交集类型,因此它扩展了EnumOptions接口。 You can call it like this:你可以这样称呼它:

asListString(MainMenuOptions.class);

This is probably one of the cases where you need to pick one between being DRY and using enums.这可能是您需要在 DRY 和使用枚举之间选择一种的情况。

Enums don't go very far as far as code reuse is concerned, in Java at least;就代码重用而言,枚举不会 go 至少在 Java 中; and the main reason for this is that primary benefits of using enums are reaped in static code - I mean static as in "not dynamic"/"runtime", rather than static :).其主要原因是使用枚举的主要好处是在 static 代码中获得 - 我的意思是static在“非动态”/“运行时”中,而不是static Although you can "reduce" code duplication, you can hardly do much of that without introducing dependency (yes, that applies to adding a common API/interface, extracting the implementation of asListString to a utility class).尽管您可以“减少”代码重复,但如果不引入依赖关系,您几乎无法做到这一点(是的,这适用于添加通用 API/接口,将asListString的实现提取到实用程序类)。 And that's still an undesirable trade-off.这仍然是一个不受欢迎的权衡。

Furthermore, if you must use an enum (for such reasons as built-in support for serialization, database mapping, JSON binding, or, well, because it's data enumeration, etc.), you have no choice but to duplicate method declarations to an extent, even if you can share the implementation: static methods just can't be inherited, and interface methods (of which getMessage would be one) shall need an implementation everywhere.此外,如果您必须使用枚举(由于内置支持序列化、数据库映射、JSON 绑定等原因,或者,因为它是数据枚举等),您别无选择,只能将方法声明复制到在某种程度上,即使您可以共享实现:static 方法不能被继承,接口方法(其中getMessage将是其中之一)需要在任何地方实现。 I mean this way of being "DRY" will have many ways of being inelegant.我的意思是这种“干”的方式会有很多不雅的方式。

If I were you, I would simply make this data completely dynamic如果我是你,我只会让这些数据完全动态化

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

This is very scalable, although it introduces the need to validate data where enums would statically prevent bad options, and possibly custom code where an enum would offer built-in support.这是非常可扩展的,尽管它引入了验证数据的需要,其中枚举将静态地防止错误选项,以及可能的自定义代码,其中枚举将提供内置支持。

It can be improved with a "category" enum, which gives static access to menu lists, and a single place for asListString() :可以使用“类别”枚举来改进它,这使 static 可以访问菜单列表,并为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());
    }
}

It should be clear that you can replace class MenuOption with a bunch of enums implementing a common interface, which should change little to nothing in MenuCategory .应该清楚的是,您可以用一堆实现通用接口的枚举替换class MenuOption ,这在MenuCategory中应该几乎没有变化。 I wouldn't do that, but it's an option.我不会那样做,但这是一个选择。

You can DRY it a little.你可以把它弄干一点。

Utils.java实用程序.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 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;
   }
   
}

Then just do this - Utils.asListString(HostSelectionMethodOptions.class);然后就这样做 - Utils.asListString(HostSelectionMethodOptions.class);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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