2011-09-08 8 views
19

可能性の重複:巨人のif-else-場合がある
Long list of if statements in Java削除する方法大の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。理想的な解決策はクロージャー/ラムダを使用することですが、残念ながら私たちはJava 8まで待たなければなりません。

+0

+1それは私の通常のアプローチです。コードをきれいにして拡張しやすくします。 Javaがlambdasを取得すると、それは美しくなります。 – linuxuser27

+0

おそらく使用されていない多くのHandlerインスタンスをMap内に保持する必要があるという事実はどうですか? – Galya

+0

これをJMHで測定しましたか?どの時点で[動的ディスパッチ](http://shipilev.net/blog/2015/black-magic-method-dispatch/)のコストがif-else分岐よりも低いかを知ることはうれしいことです。 –

2

をあなたはswitchステートメントを使用しますが、文字列の例で文を切り替えることができますJava SE 7

に実装されている最善の解決策は、command pattern

を使用することです
+1

それは悪い考えではありませんが、私はそれがより良い解決策でなければならないと思うコマンドのパターンを作る – Jorge

6

マット・ボール氏のコメントでは、コマンド・パターンを使用できます。 Runnableをクラスのコレクションを定義します。

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

次にあなたがランナブルにあなたの鍵からマップを使用することができます。最後に

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

、とのif-elseチェーンを交換してください:

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

これは一般的なArrow Anti-Patternであり、Jeffは彼の投稿hereでこれをうまく処理するいくつかのアプローチについて議論します。

7

いくつかのオプション/アイデア:それはあるよう

  • はそれを残す - それは根本的に壊れていないのです、そしてあなたは、Javaを使用している場合(switch文を使用し
  • を維持するために合理的に明確かつシンプルです7) - これはあまり得意かどうかわからない
  • Functionオブジェクトへの文字列のハッシュマップを作成します。ここで、関数オブジェクトは必要な動作をメソッドとして実装します。あなたの呼び出しコードはちょうど:hashMap.get(name).doSomething();
  • 文字列をサブグループ化することによって関数呼び出しの階層に分割します。それぞれの文字を順に取ることでこれを行うことができます。したがって、1つのブランチが 'a'で始まるすべての名前を処理します。
  • Refactor名前をStringとして渡すのではなく、名前付きオブジェクトを渡すようにします。次に、あなただけが行うことができますnamedObject.doSomething()
8

Enumでは、インスタンスごとにメソッドを持つことができます。

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+エントリ、それがないとしてもそれをすべての大きな列挙型が派遣されています)が、(100 +プットが私の意見でも厄介である)HashMapの初期化コードを回避することができます。

そして、まだ(文書化の目的のために)別のオプションは、反射のようになります。

  • ちょっと厄介=高速+ HashMapの( "セットアップ" コード:

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

    各アプローチは、トレードオフのしていますputs数百)

  • Enum = Fast + Kinda messy 2(巨大ファイル)。
  • Reflection =遅い+ランタイムエラーが発生する可能性がありますが、大型のHashMapに頼らずにきれいに分離できます。
+0

美しい分析、ありがとう! – bertie

関連する問題