问题及其解决方案
开关/案例是用大多数命令式编程语言实现的通用控制结构。 开关比一系列的if / else更具可读性。
这是一个简单的示例:
// Switch with int literal
switch (c) {
case 1: one(); break;
case 2: two(); break;
case 3: three(); break;
default: throw new UnsupportedOperationException(String.format("Operation %d is not supported", c));
}
这是此代码中的主要问题的列表:
-
int
文字(1、2、3)与执行的代码之间的关系并不明显。 - 如果不再支持其中一个值(例如2),并且此开关未相应更新,它将永远包含未使用的代码。
- 如果引入了c的新可能值(例如4),并且未相应更新开关,则代码可能会在运行时抛出
UnsupportedOperationException
而没有任何编译时通知。 - 这种开关结构倾向于在代码中重复几次,从而使问题2和3更加复杂。
最简单的解决方法是使用int常量而不是文字。 首先,让我们定义常量:
private static int ONE = 1;
private static int TWO = 2;
private static int THREE = 3;
现在,代码将如下所示:
switch (c) {
case ONE: one(); break;
case TWO: two(); break;
case THREE: three(); break;
default: throw new UnsupportedOperationException(String.format("Operation %d is not supported", c));
}
(显然,在现实生活中,常量的名称必须是自描述的)
此代码段更具可读性,但所有其他缺点仍然相关。 改进初始代码段的下一个尝试使用2004年在版本5中引入Java语言的enums
。让我们定义以下enum
:
enum Action {ONE, TWO, THREE}
现在,开关片段将稍作更改:
Action a = ...
switch (a) {
case ONE: one(); break;
case TWO: two(); break;
case THREE: three(); break;
default: throw new UnsupportedOperationException(String.format("Operation %s is not supported", a));
}
这段代码要好一点:如果从enum Action
删除其中一个元素,它将产生编译错误。 但是,如果将其他元素添加到enum Action
,则不会导致编译错误。 在这种情况下,某些IDE或静态代码分析工具可能会产生警告,但是谁在注意警告呢? 幸运的是, enum
可以声明必须由每个元素实现的抽象方法:
enum Action {
ONE { @Override public void action() { } },
TWO { @Override public void action() { } },
THREE { @Override public void action() { } },
public abstract void action();
}
现在,switch语句可以用单行替换:
Action a = ...
a.action();
此解决方案没有上面列举的任何缺点:
- 这是可读的。 该方法“附加”到
enum
元素。 如果方法含义不清楚,则可以编写所需数量的javadoc
。 调用方法的代码很简单:什么比方法调用更简单? - 在不删除实现的情况下无法删除
enum
常量,因此,如果某些功能不再相关,则不会保留任何未使用的代码。 - 如果没有实现
action()
方法,则无法添加新的enum
元素。 没有实现的代码无法编译。 - 如果需要执行多个操作,则可以在枚举中全部执行。 正如我们已经提到的,调用特定功能的代码是微不足道的,因此现在没有代码重复了。
结论
尽管开关/外壳结构是众所周知的,并且以各种编程语言广泛使用,但是其使用可能会引起很多问题。 上面描述的使用Java枚举的解决方案没有这些缺点。 本系列的下一篇文章展示了如何扩展现有enum
功能。
翻译自: https://www.javacodegeeks.com/2019/03/featured-enum-instead-switch.html