2011-09-08 76 views
19

可能重複:
Long list of if statements in Java如何去除大的if-else-IF鏈

我的任務是與一些代碼工作,並有一個巨大的if-else-IF鏈(100+ else-ifs)來檢查字符串。

什麼是一些很好的技術來更新這個代碼,以便if-else-if鏈可以縮小到更易於管理的地方。

鏈看起來是這樣的:

if(name.equals("abc")){ 
    do something 
} else if(name.equals("xyz")){ 
    do something different 
} else if(name.equals("mno")){ 
    do something different 
} ...... 
..... 
else{ 
    error 
} 
+1

的命令模式來處理這種肯定的方式,請參閱: http://stackoverflow.com/questions/1199646/long-list-of-if-statements-in-java/1199677#1199677 –

+0

究竟是什麼錯誤,除了似乎是麻煩? –

回答

16

您可以將每個分支中的代碼提取到單獨的方法,然後將這些方法轉換爲通用基礎接口的實現(我們稱之爲Handler)。之後,您可以填寫Map<String, Handler>,然後查找併爲給定的字符串執行正確的處理程序。

不幸的是,爲接口實現100多個子類需要相當多的樣板代碼,但目前在Java中沒有更簡單的方法來實現這一點。將案例作爲Enum的元素實施可能會有所幫助 - here is an example。理想的解決方案將使用閉包/ lambdas,但唉,我們必須等到Java 8的那個...

+0

+1這是我通常的做法。使代碼乾淨,易於擴展。當Java獲得lambdas時,它會使它變得美麗。 – linuxuser27

+0

那麼你需要在Map中保留很多可能不會被使用的Handler實例呢? – Galya

+0

你用JMH測量過嗎?很高興知道[dynamic dispatch](http://shipilev.net/blog/2015/black-magic-method-dispatch/)的成本低於if-else分支的成本。 –

2

你可以使用switch語句,但switch語句中使用字符串案件已在Java SE 7

最好的解決辦法是使用command pattern

實施
+1

這不是一個壞主意,但作出一個命令的模式,我認爲這應該是一個更好的解決方案 – Jorge

6

像Matt Ball在他的評論中說的,你可以使用命令模式。定義了Runnable類的集合:

Runnable task1 = new Runnable() { 
    public void run() { /* do something */ } 
}; 
Runnable task2 = // etc. 

然後你可以使用地圖從鍵可運行:

Runnable task = taskMap.get(name); 
if (task != null) { 
    task.run(); 
} else { 
    // default else action from your original chain 
} 
0

Map<String,Runnable> taskMap = new HashMap<String,Runnable>(); 
taskMap.put("abc", task1); 
taskMap.put("xyz", task2); 
// etc. 

最後,更換的if-else鏈這是一個受歡迎的Arrow Anti-Pattern,Jeff在他的文章here中討論了一些非常好的處理方法。

7

一些選項/想法:

  • 離開它,因爲它是 - 它沒有從根本上打破,是相當清楚,維護簡單
  • 使用switch語句(如果你使用的是Java 7) - 不知道這是否讓你多得
  • 創建一個字符串的HashMap到FunctionObjects其中函數對象實現所需的行爲作爲一種方法。然後你的調用代碼只是:hashMap.get(name).doSomething();
  • 通過對字符串進行子分組,將它分成函數調用的一系列函數。您可以通過依次取每個字母來完成此操作,因此一個分支處理以'a'開頭的所有名稱等。
  • 重構這樣您就不會將該名稱作爲String傳遞,而是傳遞一個命名對象。那麼你可以做namedObject.doSomething()
8

使用枚舉,你可以有一個每個實例的方法。

public enum ActionEnum { 
    ABC { 
     @Override 
     void doSomething() { 
      System.out.println("Doing something for ABC");  
     } 

    }, 
    XYZ { 
     @Override 
     void doSomething() { 
     System.out.println("Doing something for XYZ"); 
     } 
    }; 

    abstract void doSomething(); 
} 

public class MyActionClass { 

    public void myMethod(String name) { 
     ActionEnum.valueOf("ABC").doSomething(); 
    } 

} 

它仍然是有點亂(大枚舉有100多個條目,即使它它是所有調度),但可避免HashMap的初始化代碼(100個+看跌期權也是在我看來雜亂)。

而另一個選項(文檔目的)是反思:

public interface Action { 
    void doSomething(); 
} 

public class ABCAction implements Action { 
    @Override 
    public void doSomething() { 
     System.out.println("Doing something for ABC");  
    } 
} 

public class MyActionClass { 

    void doSomethingWithReflection(String name) { 
     try { 
      Class<? extends Action> actionClass = Class. 
       forName("actpck."+ name + "Action").asSubclass(Action.class); 
      Action a = actionClass.newInstance(); 
      a.doSomething(); 
     } catch (Exception e) { 
      // TODO Catch exceptions individually and do something useful. 
      e.printStackTrace(); 
     } 
    } 
} 

每一種方法都有它的權衡:

  • 的HashMap =快速+有點兒凌亂( 「建立」 碼與百放)
  • 枚舉=快+有點凌亂2(巨大的文件)。
  • 反思=更慢+運行時錯誤容易,但提供乾淨的分離,而不訴諸笨重的大HashMap。
+0

美麗的分析,謝謝! – bertie