如何为 Java 中的枚举值提供实现?
How to provide implementations for enum values in Java?
我有枚举 class,其值假设会随着时间增长,我希望添加新枚举值的用户也能在某处提供实现。
但我不确定如何强制他们提供实施,因为实施将在其他 class 中进行。
例如
public enum DayType {
SUNDAY,
MONDAY;
}
在 class
中引用
class X{
DateType dateType;
..
}
并用于其他一些 class
if (x.getDateType().equals(DayType.SUNDAY)) {
...
}else if(x.getDateType().equals(DayType.MONDAY)){
..
}
因此,如果有人添加 DateType,那么他也应该被迫在上面的 if-else 逻辑中添加实现。
如果可能的话,最好通过添加功能接口?
我无法在枚举 class 中强制实施,因为实施具有 spring 依赖性。
虽然我怀疑这可能会在编译时受到限制。一种可能更简洁的实现类似方法的方法是在此处使用 switch
:
switch (x.getDateType()) {
case MONDAY: // do something
break;
case SUNDAY: // do something
break;
default:
throw new CustomException("No implementations yet for :" + x.getDateType());
}
枚举旨在保存编译时(静态)最终值,因此动态添加更多值是不可能的(只是把它放在那里)。至于问题的另一部分,正如@nullpointer 指出的那样,最好使用 switch
子句。除了提高代码清晰度之外,如果枚举值没有为它们声明 case
语句(即,假设您省略了 default
)
,编译器会发出警告
我也不确定为用户扩展设计枚举是否是个好主意。正如@Garikai 所说,它是静态的。这会导致代码异味。
如果您希望有一个简单的选项来扩展从枚举到函数的映射,我会建议 Map<DateType, Function> map = new EnumMap<DateType, Function>(DateType.class);
然后使用 getOrDefault
以确保您将获得任何输出。
真正的答案是:不要这样做。
如果你有一些 "type",并且你知道这个 "type" 会随着时间的推移看到新的化身,需要不同的行为,那么答案就是使用抽象 class和多态性。
使用 if/else 链或 switch 语句并不重要:你有这样的想法:
if (someObject.getSomething() == whatever) { then do this } else { that }
是不好的做法!
如果有的话,您应该在工厂内使用这样的 switch 语句到 return 不同的子 class 实例,然后在这些对象上调用 "common" 方法。
您当前的方法是 A) 外部化有关内部状态的知识和 B) 还以多种方式加强依赖性。
意大利面条代码就是这样开始的!最好退后一步,考虑更多的 OOP 方法来解决这个问题!
nullpointer 选项的替代方法是将该逻辑放在枚举本身中,假设这在您的设计中有意义(如果该责任可以由 day 类型枚举承担)。
public enum DayType {
SUNDAY {
@Override
void processSomeAction(Object input) {
super.processSomeAction(input);
// process action with SUNDAY-specific logic
}
},
MONDAY;
//can be made abstract if there's no default implementation
void processSomeAction(Object input) {
// process action with default logic
}
}
这将确保对 DayType
进行更改的开发人员可以清楚地了解提供特定日期实施的需求。
调用方只需要:
x.getDateType().processSomeAction(inputIfApplicable);
您可以创建一个接口,它将由您的 Enum class 实现。在这种情况下,所有枚举都必须在接口中实现方法。
public enum DateType implements DateTypeInterface {
SUNDAY {
@Override
public void checkCondition() {
System.out.println("Implement Sunday logic");
}
},
MONDAY {
@Override
public void checkCondition() {
System.out.println("Implement Monday logic.");
}
}
}
public interface DateTypeInterface {
public void checkCondition();
}
我认为您在编译时无法选择执行您的策略。我的建议是使用单个方法(比如 action()
)创建一个接口(类似于 DateTypeAction
),然后创建一个基于 DateType
的值生成具体实现的工厂(类似于 DateTypeActionFactory.getDateTypeAction(DateType dateType)
)。在初始化阶段,您可以 运行 遍历 DateType (DateType.values()
) 的所有值,并检查每个值的 DateTypeActionFactory
中是否存在 DateTypeAction
的非空实现.如果您发现一个或多个值缺少实现,则会抛出一个异常,并显示明确的错误消息,告知缺少一个实现 is/are 并导致您的应用启动失败。这似乎是一个合理的模式。
顺便说一句,如果你采用这种模式,我有一个建议:我写了一个名为 MgntUtils 的开源 java 库,它提供了一个简单的框架(非常适合与 Spring 一起使用)有一个自我填充的工厂模式。 IE。您可以创建一个接口和一个工厂扩展库,提供父级 类,然后您的接口的每个实现都将自动插入到您的工厂中,并带有预定义的名称。我用了很多次,觉得非常方便。这是描述整个库的文章的 link:MgntUtils Open Source Java library with stack trace filtering, Silent String parsing, Unicode converter and Version comparison。寻找段落
Lifecycle management (Self-instantiating factories)
功能的简短说明。您可以在此处阅读该功能的完整构想:Non-intrusive access to "Orphaned" Beans in Spring framework. Both articles explain where to get library as well, but here are the direct links: Maven Central Repository and Github。该库附带了写得很好的 javadoc
此答案扩展了此处提倡使用多态性的其他一些答案。
为了避免在某处出现不断增长的 switch/conditional,我通常会尝试在界面中添加一个识别方法:
public enum Day {
SUNDAY, MONDAY
}
public interface DayProcessor {
Day day();
// I don't know what Days are supposed to do exactly
Object process(Object input);
}
然后我创建了一个工厂组件,其中包含所有可用的 DayProcessor
实现,这些实现由 Spring:
@Component
public class DayProcessorFactory {
@Autowired
private List<DayProcessor> dayProcessors;
public DayProcessor create(Day day) {
for (DayProcessor dayProcessor: dayProcessors) {
if (dayProcessor.day().equals(day)) {
return dayProcessor;
}
}
// DayNotFoundException extends RuntimeException
throw new DayNotFoundException(String.format("No DayProcessor found for day %s", day));
}
}
这是 Strategy Pattern 的一种形式。
另一种列出所有日期的方法是枚举它们(但是当创建新的一天时您必须更新列表):
private DayProcessor[] dayProcessors = new DayProcessor[] {new MondayProcessor(), new SundayProcessor()};
我有枚举 class,其值假设会随着时间增长,我希望添加新枚举值的用户也能在某处提供实现。 但我不确定如何强制他们提供实施,因为实施将在其他 class 中进行。 例如
public enum DayType {
SUNDAY,
MONDAY;
}
在 class
中引用class X{
DateType dateType;
..
}
并用于其他一些 class
if (x.getDateType().equals(DayType.SUNDAY)) {
...
}else if(x.getDateType().equals(DayType.MONDAY)){
..
}
因此,如果有人添加 DateType,那么他也应该被迫在上面的 if-else 逻辑中添加实现。 如果可能的话,最好通过添加功能接口?
我无法在枚举 class 中强制实施,因为实施具有 spring 依赖性。
虽然我怀疑这可能会在编译时受到限制。一种可能更简洁的实现类似方法的方法是在此处使用 switch
:
switch (x.getDateType()) {
case MONDAY: // do something
break;
case SUNDAY: // do something
break;
default:
throw new CustomException("No implementations yet for :" + x.getDateType());
}
枚举旨在保存编译时(静态)最终值,因此动态添加更多值是不可能的(只是把它放在那里)。至于问题的另一部分,正如@nullpointer 指出的那样,最好使用 switch
子句。除了提高代码清晰度之外,如果枚举值没有为它们声明 case
语句(即,假设您省略了 default
)
我也不确定为用户扩展设计枚举是否是个好主意。正如@Garikai 所说,它是静态的。这会导致代码异味。
如果您希望有一个简单的选项来扩展从枚举到函数的映射,我会建议 Map<DateType, Function> map = new EnumMap<DateType, Function>(DateType.class);
然后使用 getOrDefault
以确保您将获得任何输出。
真正的答案是:不要这样做。
如果你有一些 "type",并且你知道这个 "type" 会随着时间的推移看到新的化身,需要不同的行为,那么答案就是使用抽象 class和多态性。
使用 if/else 链或 switch 语句并不重要:你有这样的想法:
if (someObject.getSomething() == whatever) { then do this } else { that }
是不好的做法!
如果有的话,您应该在工厂内使用这样的 switch 语句到 return 不同的子 class 实例,然后在这些对象上调用 "common" 方法。
您当前的方法是 A) 外部化有关内部状态的知识和 B) 还以多种方式加强依赖性。
意大利面条代码就是这样开始的!最好退后一步,考虑更多的 OOP 方法来解决这个问题!
nullpointer 选项的替代方法是将该逻辑放在枚举本身中,假设这在您的设计中有意义(如果该责任可以由 day 类型枚举承担)。
public enum DayType {
SUNDAY {
@Override
void processSomeAction(Object input) {
super.processSomeAction(input);
// process action with SUNDAY-specific logic
}
},
MONDAY;
//can be made abstract if there's no default implementation
void processSomeAction(Object input) {
// process action with default logic
}
}
这将确保对 DayType
进行更改的开发人员可以清楚地了解提供特定日期实施的需求。
调用方只需要:
x.getDateType().processSomeAction(inputIfApplicable);
您可以创建一个接口,它将由您的 Enum class 实现。在这种情况下,所有枚举都必须在接口中实现方法。
public enum DateType implements DateTypeInterface {
SUNDAY {
@Override
public void checkCondition() {
System.out.println("Implement Sunday logic");
}
},
MONDAY {
@Override
public void checkCondition() {
System.out.println("Implement Monday logic.");
}
}
}
public interface DateTypeInterface {
public void checkCondition();
}
我认为您在编译时无法选择执行您的策略。我的建议是使用单个方法(比如 action()
)创建一个接口(类似于 DateTypeAction
),然后创建一个基于 DateType
的值生成具体实现的工厂(类似于 DateTypeActionFactory.getDateTypeAction(DateType dateType)
)。在初始化阶段,您可以 运行 遍历 DateType (DateType.values()
) 的所有值,并检查每个值的 DateTypeActionFactory
中是否存在 DateTypeAction
的非空实现.如果您发现一个或多个值缺少实现,则会抛出一个异常,并显示明确的错误消息,告知缺少一个实现 is/are 并导致您的应用启动失败。这似乎是一个合理的模式。
顺便说一句,如果你采用这种模式,我有一个建议:我写了一个名为 MgntUtils 的开源 java 库,它提供了一个简单的框架(非常适合与 Spring 一起使用)有一个自我填充的工厂模式。 IE。您可以创建一个接口和一个工厂扩展库,提供父级 类,然后您的接口的每个实现都将自动插入到您的工厂中,并带有预定义的名称。我用了很多次,觉得非常方便。这是描述整个库的文章的 link:MgntUtils Open Source Java library with stack trace filtering, Silent String parsing, Unicode converter and Version comparison。寻找段落
Lifecycle management (Self-instantiating factories)
功能的简短说明。您可以在此处阅读该功能的完整构想:Non-intrusive access to "Orphaned" Beans in Spring framework. Both articles explain where to get library as well, but here are the direct links: Maven Central Repository and Github。该库附带了写得很好的 javadoc
此答案扩展了此处提倡使用多态性的其他一些答案。
为了避免在某处出现不断增长的 switch/conditional,我通常会尝试在界面中添加一个识别方法:
public enum Day {
SUNDAY, MONDAY
}
public interface DayProcessor {
Day day();
// I don't know what Days are supposed to do exactly
Object process(Object input);
}
然后我创建了一个工厂组件,其中包含所有可用的 DayProcessor
实现,这些实现由 Spring:
@Component
public class DayProcessorFactory {
@Autowired
private List<DayProcessor> dayProcessors;
public DayProcessor create(Day day) {
for (DayProcessor dayProcessor: dayProcessors) {
if (dayProcessor.day().equals(day)) {
return dayProcessor;
}
}
// DayNotFoundException extends RuntimeException
throw new DayNotFoundException(String.format("No DayProcessor found for day %s", day));
}
}
这是 Strategy Pattern 的一种形式。
另一种列出所有日期的方法是枚举它们(但是当创建新的一天时您必须更新列表):
private DayProcessor[] dayProcessors = new DayProcessor[] {new MondayProcessor(), new SundayProcessor()};