web-dev-qa-db-ja.com

if / elseロジックのリファクタリング

次のようなif/elseロジックの1000行メソッドを持つJavaクラスがあります:

if (userType == "admin") {
     if (age > 12) {
          if (location == "USA") {
               // do stuff
          } else if (location == "Mexico") {
               // do something slightly different than the US case
          }
     } else if (age < 12 && age > 4) {
          if (location == "USA") {
               // do something slightly different than the age > 12 US case
          } else if (location == "Mexico") {
               // do something slightly different
          }
     }
 } else if (userType == "student") {
     if (age > 12) {
          if (location == "USA") {
               // do stuff
          } else if (location == "Mexico") {
               // do something slightly different than the US case
          }
     } else if (age < 12 && age > 4) {
          if (location == "USA") {
               // do something slightly different than the age > 12 US case
          } else if (location == "Mexico") {
               // do something slightly different
          }
     }

これをより管理しやすいものにリファクタリングするにはどうすればよいですか?

35
David

Strategies を使用する必要があります。列挙型内に実装されている可能性があります。例:

enum UserType {
  ADMIN() {
    public void doStuff() {
      // do stuff the Admin way
    }
  },
  STUDENT {
    public void doStuff() {
      // do stuff the Student way
    }
  };

  public abstract void doStuff();
}

コードの最も外側の各ifブランチ内のコード構造はほとんど同じように見えるため、リファクタリングの次のステップでは、 テンプレートメソッド を使用してその重複を除外することをお勧めします。または、場所(場合によっては年齢)も戦略に変えることができます。

Update:Java4では、 typesafe enum を手動で実装し、単純な古いサブクラス化を使用してさまざまな戦略を実装できます。

26
Péter Török

このコードで最初にすることは、型AdminStudentを作成することです。どちらも基本型Userを継承しています。これらのクラスには、このロジックの残りの部分を非表示にするdoStuff()メソッドが必要です。

経験則として、タイプをオンに切り替えたときにいつでも、代わりにポリモーフィズムを使用できます。

12
Bill the Lizard

数千?おそらく、ルールエンジンが必要です。 Droolsは実行可能な代替手段になる可能性があります。

または、ケースごとにすべての「少し異なることをする」ロジックをカプセル化するコマンドパターン。年齢、場所、その他の要素の連結をキーとして、各コマンドをマップに保存します。コマンドを検索して実行すると、完了です。素敵できれい。

マップは構成として保存し、起動時に読み込むことができます。新しいクラスを追加して再構成することにより、新しいロジックを追加できます。

9
duffymo

まず、userTypeと場所に列挙型を使用します。次に、switchステートメントを使用できます(読みやすさが向上します)

2番目-より多くの方法を使用します。

例:

switch (userType) {
  case Admin: handleAdmin(); break;
  case Student: handleStudent(); break;
}

以降

private void handleAdmin() {
  switch (location) {
    case USA: handleAdminInUSA(); break;
    case Mexico: handleAdminInMexico(); break;
  }
}

さらに、重複するコードを識別し、追加のメソッドに配置します。

[〜#〜]編集[〜#〜]

Java enumなしでコーディングするように強制された場合(Java 1.4.2を使用せざるを得ない場合など)、enumの代わりに「final static」を使用するか、何かのようなもの:

  if (isAdmin(userType)) {
    handleAdmin(location, age);
  } else if (isStudent(userType)) {
    handleStudent(location, age));
  }

//...

private void handleAdmin(String location, int age) {
  if (isUSA(location)) {
    handleAdminInUSA(age);
  } else if (isUSA(location)) {
    handleAdminInMexico(age);
  }
}

//...

private void handleAdminInUSA(int age) {
  if (isOldEnough(age)) {
    handleAdminInUSAOldEnough();
  } else if (isChild(age)) {
    handleChildishAdminInUSA(); // ;-)
  } //...
}
6
Andreas_D

おそらく最初に、コードdoStuffとdoSimilarStuffをパラメーター化できるかどうかを最初に確認します。

2
tkr

ブロック内のコードがいくつかの標準パターンに収まる場合、列(type、location、minAge、maxAge、action)を含むテーブルを作成します。ここで、 'action'は、実行する処理のタイプを示す列挙型です。理想的には、このテーブルはデータファイルから読み取られるか、SQLに保持されます。

次に、Javaコードでテーブルを検索して、ユーザーに対して実行するアクションを決定します。

2
Walter Mundt

Chain ofResponsibilityパターンを使用できます。

リファクタリングif-elseステートメントを、たとえば、インターフェースIUserControllerのクラスにクラス化します。

リスト、ツリー、または適切なデータ構造内でチェーンを初期化し、このチェーンで必要な機能を実行します。 Builderパターンを使用して、前述のデータ構造を作成できます。戦略パターンに似ていますが、Chain of Responsibilityパターンでは、チェーン内のインスタンスがリンクされたインスタンスを呼び出すことができます。

さらに、戦略パターンを使用して、場所固有の機能をモデル化できます。それが役に立てば幸い。

2
baris.aydinoz

これのリスクは見苦しいだけでなく、エラーが発生しやすいことです。しばらくすると、条件が重複するリスクに遭遇する可能性があります。

ユーザータイプによって条件を実際に区別できる場合は、少なくとも各条件の本体を個別の関数に分割できます。タイプに基づいてチェックし、そのタイプに固有の適切な関数を呼び出すようにします。もっとOO解決策は、各ユーザーをクラスとして表し、いくつかの計算メソッドをオーバーライドして、年齢に基づいて値を返すことです。クラスを使用できないが、少なくとも列挙型を使用できる場合、その後、列挙型でより適切なswitchステートメントを実行できるようになります。文字列のスイッチはJava 7でのみ提供されます。

私が心配しているのは、重複する状況です(たとえば、いくつかの共有ルールを持つ2つのユーザータイプなど)。その場合は、データを読み取りおよび保守する外部ファイル(テーブルなど)として表す方がよい場合があります。コードは基本的に、このデータで適切なルックアップを行うドライバーとして動作します。セットする。これは、複雑なビジネスルールの一般的なアプローチです。これは、大量のコードを実行して維持することを誰も望んでいないためです。

2
Uri

ビジターパターンを見てください。ポリモーフィズムを利用しますが、後で新しいケースを追加する方が簡単であるという点で、もう少し柔軟性があります。

欠点は、状態情報を別のインスタンスに変換する方法が必要になることです。利点は、継承階層を変更せずに動作を追加するためのより明確な方法です。

1
Kelly S. French

これらのケースをオブジェクトメソッドに分割する必要があります。これらの文字列と数値はデータベースから引き出されていると思います。巨大なネストされた条件付きロジックで生の形式でそれらを使用する代わりに、これらのデータを使用して、目的の相互作用をモデル化するオブジェクトを構築する必要があります。 StudentRoleおよびAdminRoleサブクラスを持つUserRoleクラス、USAおよびMexicoサブクラスを持つRegionクラス、および適切に分割されたサブクラスを持つAgeGroupクラスについて考えてみます。

このオブジェクト指向構造を配置すると、よく理解されているオブジェクト指向設計パターンを利用して、このロジックをリファクタリングできるようになります。

1
bshields

userTypeenumにして、「少し違うことをする」アクションをすべて実行するメソッドを与えることができます。

1
Syntactic

より多くの情報なしでは良い答えはありません

しかし、公正な推測はこれです:OOを使用してください

最初にユーザーを定義し、管理者、学生、その他すべてのタイプのユーザーを定義してから、ポリモーフィズムに残りの処理を任せます

1
Peter

変数名だけに基づいて、User(またはuserType変数を持つもの)をAdminUserStudentUser(および場合によっては他の変数)にサブクラス化し、 ポリモーフィズム を使用する必要があると思います。

1
Hank Gay

使用OOP概念:これはデザインの残りの部分に依存しますが、おそらくuserインターフェース、StudentAdminが必要です。インターフェースはそれを拡張し、UsaStudentMexicoStudentUsaAdminMexicoAdmin実装はいくつかのことを行います。Userインスタンスを保持して呼び出すだけですそのdoStuffメソッド。

0
Amir Rachum