この方法:
boolean containsSmiley(String s) {
if (s == null) {
return false;
}
else {
return s.contains(":)");
}
}
同等に書くことができます:
boolean containsSmiley(String s) {
if (s == null) {
return false;
}
return s.contains(":)");
}
私の経験では、2番目の形式は、特により複雑なメソッド(そのような出口点がいくつかある場合がある)でより頻繁に見られ、「スロー」と「リターン」にも同じことが当てはまります。しかし、最初の形式は間違いなくコードの条件付き構造をより明確にします。どちらか一方を優先する理由はありますか?
(関連: 関数にはreturnステートメントが1つだけ必要ですか? )
私の経験では、コードによって異なります。私が何かを「守っている」なら、私はやります:
if (inputVar.isBad()) {
return;
}
doThings();
要点は明らかです。そのステートメントがfalseの場合、関数を続行したくありません。
一方、複数のオプションを持ついくつかの関数があり、その場合は次のように記述します。
if (inputVar == thingOne) {
doFirstThing();
} else if (inputVar == secondThing) {
doSecondThing();
} else {
doThirdThing();
}
次のように書くこともできますが:
if (inputVar == thingOne) {
doFirstThing();
return;
}
if (inputVar == thingTwo) {
doSecondThing();
return;
}
doThingThree();
return;
それは実際に、コードが何をしているかを最も明確に示す方法に帰着します(必ずしもどのビットのコードが最も短いか、またはインデントが最も少ないかとは限りません)。
その場合のelse
は冗長になるだけでなく、関数のメインコードに不要なインデントが作成されます。
これは Guard Clause と呼ばれるパターンです。すべてのチェックを事前に実行して、ネストされた条件を減らして読みやすくします。
リンクから:
double getPayAmount() {
double result;
if (_isDead) {
result = deadAmount();
} else {
if (_isSeparated) {
result = separatedAmount();
} else {
if (_isRetired) {
result = retiredAmount();
} else {
result = normalPayAmount();
}
}
}
return result;
}
Guard句を使用すると、次の結果が表示されます。
double getPayAmount() {
if (_isDead) return deadAmount();
if (_isSeparated) return separatedAmount();
if (_isRetired) return retiredAmount();
return normalPayAmount();
};
あなたはこれを至る所で見るでしょう:
if (condition) {
return var;
}
// by nature, when execution reaches this point, condition can only be false,
// therefore, the else is unnecessary
return other_var;
ほとんどの場合、else句の追加はこの場合に不要であるだけでなく、多くの場合、コンパイラによってoptimized awayを取得します。
コンピューターがこのコードをどのように考えているかを考えてみてください(マシンコードの観点から、ここではデモンストレーションのために疑似コードに簡略化しています)。
0x00: test [condition]
0x01: if result of test was not true, goto [0x04]
0x02: Push [var] onto stack
0x03: goto [0x05]
0x04: Push [other_var] onto stack
0x05: return from subroutine
コード(これも疑似コードであり、アセンブリではありません)は、if/then/else
条件に対してまったく同じように動作します。
多くの人々は、プログラマーが自分のコードを通過する可能性のあるすべてのパスを考えなければならないため、関数に複数の出口点がある可能性があるとは考えにくい、あるいは混乱を招く習慣だと考えています。別の方法は次のとおりです。
return (condition) ? var : other_var;
これによりコードが簡素化され、新しい出口点が作成されることはありません。
私はそれをこのように書くことを好みます:
boolean containsSmiley(String s) {
return s != null && s.contains(":)");
}
人間が読みやすいので、最初のオプションを選択します。
類推として、次の2つの文を比較してください。そして、「今日雨が降っているなら、傘を取って、サングラスを取ってください」。最初の文は、質問の最初のコードブロック、2番目のブロック、2番目のブロックに対応しています。最初の方がはるかに明確で読みやすいですよね?
コーディングスタイルに関する「議論」のように、正しい答えはありません。私はこれらの考慮事項を適用することを好みます:
コードはすべての状況で期待どおりに機能しますか。 (最小の驚きの原則)
次の開発者(私または他の誰か)が何をしていて、その理由を理解できるか.
変更に関してコードがどれほど壊れやすいか。
Isは必要なだけのシンプルなもので、これ以上はありません。つまりエンジニアリングの過不足はありません。
上記の条件を満たしたことに満足したら、残りは一般的にうまく機能します。
他の誰かがおそらくこれにすでに気付いているでしょうが、文字列が期待されるところでは一般的にnull値を使用しないことをお勧めします。誰かがnull値を渡さないようにするチェックが本当に必要な場合は、アサート(開発時)またはユニットテスト(デプロイ)を使用できます。
_boolean containsSmiley(String s) {
assert s != null : "Quit passing null values, you moron.";
return s.contains(":)");
}
_
私は一般的な経験則に切り替えました:決して。ずっと。外部API呼び出しで明示的に要求されない限り、null値を渡します。 2番目:外部メソッドがnull値を返す可能性がある場合は、それを適切なnull以外の値(空の文字列など)に置き換えるか、きちんとしたチェックを追加します。繰り返しのif (thing == null)
チェックに飽き飽きしています。
しかし、それは少しおかしいです。私はtopとguard句に短い条件を設定し、プログラムのフローで要求されない場合はelseを削除するのが好きです。
より良いので。 '{' '}'を使用していくつかのレベルのネストを作成することもできますが、それだけでは誰も実際にはそれを行いません。
else
は冗長です。また、一部のIDE(Eclipse)および分析ツール(おそらくFindBugs)は、警告またはエラーとしてフラグを立てる場合があるため、プログラマーはそれを削除する可能性があります。
それは宗教的な議論であり、結局それは問題ではありません。状況によっては、最初のフォームの方が読みやすいとさえ主張します。 if-elseif-elseif-else
に大量のコードがある場合、一見するとデフォルトの戻り値が何であるかを確認するのが簡単です。
if (s == null) {
return false;
}
else if (s.Contains(":))")) {
return true;
}
else if (s.Contains(":-(")) {
return false;
}
return s.contains(":)");
Occamのかみそり は、「エンティティは必要以上に乗算してはならない」という原則です。
if
ステートメントは、null値を受信しないという契約/期待を確認/実施しています。そのため、達成しようとしていることの実際のロジックとは関係がないため(このケースは非常に単純ですが)、他の関数から分離した方がいいと思います。
ただし、ほとんどの場合、コードの目的はできるだけ明示的にすることです。関数を他の人が読みやすくするために再構築できるものがあれば、それを行います。プロのプログラマーとして、あなたの目標は本当にあなたの後にあなたのコードを維持しなければならない人たちのためにプログラムすることであるべきです(2年後の自分を含む...)。彼らを助けるためにあなたができることは何でもする価値があります。
メンテナンスの観点からは、複数の出口よりも1つの出口を優先します。最終結果は、n個の出口点ではなく1つの出口点で変更(または装飾)できます。
単純/短ければ2番目の形式。これは必ずしも明確であるとは限りません。私はあなたがあなたが最も明確であると思うことをすることを提案します。個人的に私は書くでしょう。
static boolean containsSmiley(String s) {
return s != null && s.contains(":)");
}
そのような状況で他に使用されている場合、Eclipseにはオプションの(デフォルトではオフになっている)警告があるためです;)。
最初の形式は単純です。値を返すと、自動的に現在の関数のスコープを離れて呼び出し元に戻るため、その後のコードはIFステートメントがtrueと評価されない場合にのみ実行されます。その後、何でも返します。
読みやすさを主張します。コードの画面をスキャンして、コードの動作を理解しようとする場合、それは開発者に対する視覚的なプロンプトです。
...しかし、私たち全員がコードを非常によくコメントしているので、それは本当に必要ではありませんよね? :)
私の意見では、2番目の方がより理にかなっています。スイッチのような「デフォルト」アクションとして機能します。それが他のどの出口点とも一致しない場合は、それを行います。あなたは本当に他に必要はありません。関数全体がifとelseifのみである場合、1つの巨大な条件であるため、elseはそこに意味があります。その中で実行される複数の条件と他の関数がある場合、最後のデフォルトの戻り値が使用されます。
Elseを持つことは正しく、ロジックと実行可能性の点で何も問題はありませんが、if/elseスコープの外側に関数がreturnステートメントを持たない最初のWTFの瞬間は避けたいです。
ご覧のとおり、読みやすさについては人によって意見が異なります。一部の人々は、コードの行数が少ないとコードが読みやすくなる傾向があると考えています。他の人たちは、2番目の形式の対称性により、2番目の形式が読みやすくなると考えています。
私の見解では、おそらく、両方の見方は正しいのです...それらを保持する人々にとって。そして当然の結果として、誰もが最適に読みやすいコードを書くことはできません。したがって、最善のアドバイスは、義務付けられたコーディング標準が何をすべきかに従っており(これについて何か言う場合)、一般的には常識を使用することです。 (あなたが彼のやり方が「正しい」と主張するいくつかの騒々しいニットに悩まされているなら...ただ流れに行くだけです。)
それは、プログラマの意図についての証拠をもたらす次のいずれかを書くのと同じだからです。
boolean containsSmiley(String s) {
if (s == null) // The curly braces are not even necessary as the if contains only one instruction.
return false;
return s.contains(":)");
}
またはこれでも:
boolean constainsSMiley(String s) {
return string.IsNullOrEmpty(s) ? false : s.Contains(":)");
}
これらの2つの形式は次のとおりです。
まあ、理由のいくつかは単なる慣習ですが、上記のフォームには1つの利点があります...
Returnステートメントをコーディングするときは、通常、最後のステートメントをデフォルトの戻り値にします。これは主にリファクタリング時に役立ちます。else句は他の構造に巻き込まれる傾向があるか、誤ってツリーの奥に移動する可能性があります。