使用枚举ENUM替换Switch或If-Else

19-03-03 banq
         

Switch/case是在大多数命令式编程语言中实现的通用控制结构。Switch被认为比if/else系列更具可读性。

这是一个简单的例子:

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

以下是此代码中的主要问题列表:

  1. int类型的字符号(1,2,3)和执行的代码之间的关系并不明显。
  2. 如果其中一个值(例如2)不再受支持,并且此switch未相应更新,则它将永远包含未使用的代码。
  3. 如果引入了新的可能值c(例如4)并且Swtich没有相应更新,则代码可能会在运行时抛出UnsupportedOperationException,而不会在编译时发生任何通知。
  4. 这种Switch结构往往在代码中被重复多次,使问题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));
}

(显然在现实生活中,常量的名称必须是自描述的)这个片段更具可读性,但所有其他缺点仍然相关。

枚举Enum

进一步改进初始代码片段的尝试是使用2004年版本5中引入Java语言的枚举。

让我们定义以下枚举: 

enum Action {ONE, TWO, THREE}

现在,Switch代码段会略有变化: 

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

这段代码更好一点:如果从枚举Action中删除了其中一个元素,它将产生编译错误。 但是,如果向枚举Action添加了其他元素,则不会导致编译错误  。 在这种情况下,某些IDE或静态代码分析工具可能会产生警告,但是谁会关注警告你?幸运的是,枚举可以声明必须由每个元素实现的抽象方法:

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

该解决方案没有上面列举的任何缺点:

  1. 它是可读的。该方法被“附加”到枚举元素; 如果方法含义不清楚,可以根据需要编写尽可能多的javadoc。调用方法的代码是微不足道的:什么可以比方法调用更简单?
  2.  如果不删除实现,就无法删除  枚举常量,因此如果某些功能不再相关,则不会保留未使用的代码。
  3.  如果没有实现方法action(),则无法添加新的  枚举元素。没有实现的代码会无法编译。 
  4. 如果需要多个操作,则可以在枚举中实现所有操作  。正如我们已经提到的,调用特定函数的代码是微不足道的,所以现在没有代码重复。