使用列舉ENUM替換Switch或If-Else

banq發表於2019-03-03

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. 如果需要多個操作,則可以在列舉中實現所有操作  。正如我們已經提到的,呼叫特定函式的程式碼是微不足道的,所以現在沒有程式碼重複。 

相關文章