web-dev-qa-db-ja.com

EnumのJavaでのswitch(this)アンチパターンまたは悪い習慣は何ですか?

仕事で問題にぶつかり、列挙型がどのように成長し、ビジネスロジックを格納しているかを確認しました。彼らが成長するにつれて、コンストラクタは大きく成長しました。ある時点で、10番目のブール型パラメーター(およびブール値以外のさまざまなパラメーターが既に含まれている)をコンストラクターに置く代わりに、すべての初期化を追加のパラメーターで展開すると、新しいパラメーターを作成しますが、実際にはそうではありませんused-コードを読みやすくするための構造。

ここに例があるので、代わりに:

public enum A {
  A1(true, true, false),
  A2(true, false, true),
  A3(false, false, false);

private A(boolean firstParam, boolean secondParam, boolean thirdParam){
    ... usual constructor ...
}

... getters

だから代わりに私は次のことをしました:

public enum A {
  A1,
  A2,
  A3;

... no need for special constructor

public boolean isFirstParam(){
  switch(this){
    case A1:
    case A2:
      return true;
    default:
      return false;
  }
}

public boolean isSecondParam(){
  switch(this){
    case A1:
      return true;
    default:
      return false;
  }
}

public boolean isThirdParam(){
  switch(this){
    case A2:
      return true;
    default:
      return false;
  }
}

これまでのところ、それを使用した人は誰でも気に入っています。しかし、それを知らない同僚の中には悪い反応を示した人もいました。

  • ソナーはswitch(this)ステートメントを見て不満でした。
  • 彼らはコンセプト自体を好まなかった。

私の質問は、私がそれを使用すべきではない本当の理由はありますか?複雑な列挙型があると、より保守しやすくなります。特別な場所ではなく、列挙型を切り替えるだけで、特別なことは何も使用しなかったと思います。私は正しいですか?

7
CsBalazsHungary

他の選択肢は、インスタンスごとにメソッドをオーバーライドすることです:

public enum A {
  A1{
    @Override public boolean isFirstParam(){ return true; }
    @Override public boolean isSecondParam(){ return true; }
    @Override public boolean isThirdParam(){ return false; }
  },
  A2{
    @Override public boolean isFirstParam(){ return true; }
    @Override public boolean isSecondParam(){ return false; }
    @Override public boolean isThirdParam(){ return false; }
  },
  A3{
    @Override public boolean isFirstParam(){ return false; }
    @Override public boolean isSecondParam(){ return false; }
    @Override public boolean isThirdParam(){ return false; }
  };

  public abstract boolean isFirstParam();
  public abstract boolean isSecondParam();
  public abstract boolean isThirdParam();
}

https://docs.Oracle.com/javase/8/docs/technotes/guides/language/enums.html には次のように書かれています。

列挙型定数に動作を追加するという考えは、さらに一歩進めることができます。一部のメソッドでは、各列挙定数に異なる動作を与えることができます。列挙定数をオンにしてこれを行う1つの方法。以下は、定数が4つの基本的な算術演算を表し、evalメソッドが演算を実行する列挙型の例です。

public enum Operation {
    PLUS, MINUS, TIMES, DIVIDE;

    // Do arithmetic op represented by this constant
    double eval(double x, double y){
        switch(this) {
            case PLUS:   return x + y;
            case MINUS:  return x - y;
            case TIMES:  return x * y;
            case DIVIDE: return x / y;
        }
        throw new AssertionError("Unknown op: " + this);
    }
}

これは正常に機能しますが、throwステートメントがないとコンパイルできません。さらに悪いことに、新しい定数をOperationに追加するたびに、switchステートメントに新しいケースを追加することを忘れないでください。忘れた場合、evalメソッドは失敗し、前述のthrowステートメントを実行します

これらの問題を回避するいくつかのメソッドに対して、各列挙型定数に異なる動作を与える別の方法があります。 enum型でメソッドの抽象を宣言し、各定数の具象メソッドでオーバーライドできます。このような方法は、定数固有の方法として知られています。このテクニックを使用してやり直した前の例を次に示します。

public enum Operation {
  PLUS   { double eval(double x, double y) { return x + y; } },
  MINUS  { double eval(double x, double y) { return x - y; } },
  TIMES  { double eval(double x, double y) { return x * y; } },
  DIVIDE { double eval(double x, double y) { return x / y; } };

  // Do arithmetic op represented by this constant
  abstract double eval(double x, double y);
}

個人的には、例外をスローしてすべての可能性を明示的に処理することを気にしません。exceptスイッチが列挙自体に配置される場合。 IDEは、すべてのケースを処理しない場合に警告を表示します(コンパイルできないようにエラーに変換するように構成することもできます)。プロジェクトの警告を表示しない場合私がやろうとしているのは、どのファイルのswitchステートメントを更新する必要があるかすぐにわかるでしょう。

14
Darsstar