web-dev-qa-db-ja.com

「else」は、制御フローによって冗長になる状況で使用する必要がありますか?

次の例のようなコードに遭遇することがあります(この関数が実際に行うことは、この質問の範囲外です)。

function doSomething(value) {
  if (check1(value)) {
    return -1;
  }
  else if (check2(value)) {
    return value;
  }
  else {
    return false;
  }
}

ご覧のとおり、ifelse ifおよびelseステートメントは、returnステートメントと組み合わせて使用​​されます。これはカジュアルな観察者にはかなり直感的に思えますが、else- sを削除してコードを次のように単純化する方が(ソフトウェア開発者の観点から)よりエレガントになると思います。

function doSomething(value) {
  if (check1(value)) {
    return -1;
  }
  if (check2(value)) {
    return value;
  }
  return false;
}

returnステートメント(同じスコープ内)に続くすべてが実行されることはないため、上記のコードは意味的に最初の例と同じになります。

上記のどれが優れたコーディングプラクティスにより適していますか?コードの読みやすさに関して、どちらの方法にも欠点はありますか?

編集:参考として この質問 を使用して重複した提案が行われました。私の質問は別のトピックに触れていると思います。他の質問で提示されているような重複したステートメントを回避することについて質問しているのではないからです。どちらの質問も、わずかに異なる方法ではありますが、繰り返しを減らすことを目指しています。

19
rhino

私はelseのないものが好きで、その理由は次のとおりです。

_function doSomething(value) {
  //if (check1(value)) {
  //  return -1;
  //}
  if (check2(value)) {
    return value;
  }
  return false;
}
_

それをすることは何も壊さなかったので、想定されていなかったからです。

すべての形式での相互依存を嫌います(関数の名前付けcheck2()を含む)。分離できるすべてのものを分離します。 elseが必要になることもありますが、ここではわかりません。

23
candied_orange

コードのセマンティクスに依存すると思います。 3つのケースが互いに依存している場合は、明示的に述べてください。これにより、コードが読みやすくなり、他の人が理解しやすくなります。例:

if (x < 0) {
    return false;
} else if (x > 0) {
    return true;
} else {
    throw new IllegalArgumentException();
}

ここでは、3つのケースすべてでxの値に明らかに依存しています。最後のelseを省略した場合、これは不明確になります。

ケースが互いに直接依存していない場合は、省略します。

if (x < 0) {
    return false;
}
if (y < 0) {
    return true;
}
return false;

これをコンテキストのない単純な例で示すのは難しいですが、私の理解を深めていただければ幸いです。

28
André Stannek

私は2番目のオプション(else ifを使用せずにifsを分離し、returnを初期化する)を好む[〜#〜] but [〜#〜]つまり- コードブロックが短い限り

コードブロックが長い場合は、else ifを使用することをお勧めします。そうしないと、コードが持ついくつかの出口ポイントを明確に理解できないためです。

例えば:

function doSomething(value) {
  if (check1(value)) {
    // ...
    // imagine 200 lines of code
    // ...
    return -1;
  }
  if (check2(value)) {
    // ...
    // imagine 150 lines of code
    // ...
    return value;
  }
  return false;
}

その場合は、else ifを使用し、戻りコードを変数に格納し、最後に1つの戻り文のみを含めることをお勧めします。

function doSomething(value) {
  retVal=0;
  if (check1(value)) {
    // ...
    // imagine 200 lines of code
    // ...
    retVal=-1;
  } else if (check2(value)) {
    // ...
    // imagne 150 lines of code
    retVal=value;
  }
  return retVal;
}

しかし、関数が短くなるように努力する必要があるので、最初のコードスニペットのように、関数を短くし、早期に終了すると思います。

6

両方を異なる状況で使用しています。検証チェックでは、elseを省略します。制御フローでは、elseを使用します。

bool doWork(string name) {
  if(name.empty()) {
    return false;
  }

  //...do work
  return true;
}

bool doWork(int value) {
  if(value % 2 == 0) {
    doWorkWithEvenNumber(value);
  }
  else {
    doWorkWithOddNumber(value);
  }
}

最初のケースは、すべての前提条件を最初にチェックし、それらを邪魔にならないようにして、実行する実際のコードに進むようなものです。そのため、本当に実行したいジューシーなコードは、関数のスコープに直接属しています。それが機能の本質です。
2番目のケースは、両方のパスが実行される有効なコードであり、いくつかの条件に基づいて他のパスと同じように機能に関連していることを示しています。そのため、それらは互いに類似したスコープレベルに属しています。

4
Altainia

私がこれまで本当に気にした唯一の状況は次のようなコードです:

List<?> toList() {
    if (left != null && right != null) {
        Arrays.asList(merge(left, right));
    }
    if (left != null) {
        return Arrays.asList(left);
    }
    if (right != null) {
        return Arrays.asList(right);
    }
    return Arrays.asList();
}

ここに明らかに何か問題があります。問題は、returnを追加する必要があるか、Arrays.asListを削除する必要があるかが明確でないことです。これを修正するには、関連するメソッドを詳細に調査する必要があります。常に完全なif-elseブロックを使用することで、このあいまいさを回避できます。この:

List<?> toList() {
    if (left != null && right != null) {
        Arrays.asList(merge(left, right));
    } else if (left != null) {
        return Arrays.asList(left);
    } else if (right != null) {
        return Arrays.asList(right);
    } else {
        return Arrays.asList();
    }
}

最初のifに明示的な戻り値を追加しない限り、(静的にチェックされた言語で)コンパイルされません。

一部の言語では、最初のスタイルさえ許可されていません。私はそれを厳密に命令的なコンテキストで、または長いメソッドの前提条件でのみ使用しようとします:

List<?> toList() {
    if (left == null || right == null) {
        throw new IllegalStateException()
    }
    // ...
    // long method
    // ...
}
0
Banthar