機能から早期に戻るか、IFステートメントを使用する必要がありますか? [閉まっている
https://softwareengineering.stackexchange.com/questions/18454
-
22-10-2019 - |
質問
私はこの種の機能を両方の形式でよく書きましたが、ある形式が別の形式よりも優先されるかどうか疑問に思っていました。
public void SomeFunction(bool someCondition)
{
if (someCondition)
{
// Do Something
}
}
また
public void SomeFunction(bool someCondition)
{
if (!someCondition)
return;
// Do Something
}
私は通常、最初のものでコーディングします。それはコーディング中の脳の動作ですが、すぐにエラーの処理を処理するので2番目のものを好むと思います。読みやすいと思います。
解決
私は2番目のスタイルが好きです。最初に無効なケースを邪魔にならないようにして、必要に応じて例外を終了するか、上昇させて、空白行をそこに入れてから、メソッドの「実際の」本文を追加します。読みやすいと思います。
他のヒント
間違いなく後者。前者は今は悪く見えませんが、より複雑なコードを取得したとき、誰もこれを考えるとは想像できません。
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
int retval = SUCCESS;
if (someCondition)
{
if (name != null && name != "")
{
if (value != 0)
{
if (perms.allow(name)
{
// Do Something
}
else
{
reval = PERM_DENY;
}
}
else
{
retval = BAD_VALUE;
}
}
else
{
retval = BAD_NAME;
}
}
else
{
retval = BAD_COND;
}
return retval;
}
より読みやすい
public int SomeFunction(bool cond1, string name, int value, AuthInfo perms)
{
if (!someCondition)
return BAD_COND;
if (name == null || name == "")
return BAD_NAME;
if (value == 0)
return BAD_VALUE;
if (!perms.allow(name))
return PERM_DENY;
// Do something
return SUCCESS;
}
私は、単一の出口ポイントの利点を理解していなかったことを完全に認めています。
場合によります - 一般的に、私は邪魔にならないようにして、大量のコードを動かして、機能から早期に脱出するつもりはありません。コンパイラは一般的に私のためにそれを世話します。そうは言っても、上部に必要な基本的なパラメーターがある場合、そうでなければ続行できない場合は、早めにブレイクアウトします。同様に、条件が巨人を生成する場合 if
機能のブロック私は、その結果としても早くブレークアウトします。
とはいえ、関数が呼び出されたときに何らかのデータが必要な場合、私は通常、戻っているのではなく、例外をスローする(例を参照)になります。
public int myFunction(string parameterOne, string parameterTwo) {
// Can't work without a value
if (string.IsNullOrEmpty(parameterOne)) {
throw new ArgumentNullException("parameterOne");
}
if (string.IsNullOrEmpty(parameterTwo)) {
throw new ArgumentNullException("parameterTwo");
}
// ...
// Do some work
// ...
return value;
}
私は早期の帰りを好みます。
1つのエントリポイントと1つの出口ポイントがある場合は、常に頭の中のコード全体を出口ポイントまで追跡する必要があります(他のコードの一部が他の何かをしているかどうかはわかりません。存在するまでそれを追跡する必要があります)。あなたは、どのブランチが最終結果を決定するかをすることはありません。これは従うのが難しいです。
1つのエントリと複数が存在すると、結果が得られたときに戻り、誰も他に何もしないことを確認するためにそれを追跡することを気にしないでください(戻ってから他に何もありません。メソッドボディをより多くのステップに分割するようなものであり、結果を返す可能性があるか、次のステップに運を試す可能性がある各ステップがあります。
手動でクリーンアップする必要があるCプログラミングでは、1点のリターンのために多くのことが言われることがあります。何かをきれいにする必要がなくなったとしても、誰かがあなたの機能を編集し、何かを割り当て、戻る前にそれをきれいにする必要があるかもしれません。それが起こった場合、それはすべての返品ステートメントを調べる悪夢の仕事になります。
C ++プログラミングでは、デストラクタがあり、今でもスコープエキシットガードがあります。これらはすべて、コードが最初に例外に安全であることを確認するためにここにいる必要があるため、コードは早期出口から十分に保護されているため、そうすることは論理的な欠点がなく、純粋にスタイルの問題です。
「最終的に」ブロックコードが呼び出されるかどうか、そして最終的なことが何かが起こることを確実にする必要がある状況を処理できるかどうか、私はJavaについて十分に知識がありません。
C#私は確かに答えることができません。
D-Languageは、適切な組み込みスコープエキシットガードを提供するため、早期の出口に適しているため、スタイル以外の問題を提示する必要はありません。
もちろん、機能はそもそもそれほど長くはないはずであり、巨大なスイッチステートメントがある場合は、おそらくコードもひどく考慮されています。
初期のリターンは勝ちます。彼らは醜いように見えるかもしれませんが、ビッグよりもはるかに醜い if
ラッパー、特にチェックする複数の条件がある場合。
両方を使用します。
もしも DoSomething
コードの3〜5行で、最初のフォーマット方法を使用してコードは美しいように見えます。
しかし、それよりも多くの行がある場合は、2番目の形式を好みます。開閉ブラケットが同じ画面にないのが好きではありません。
シングルエントリシングルエキシットの典型的な理由は、そうでなければ正式なセマンティクスが驚くほどugいになることです(同じ理由が有害と見なされた)。
別の言い方をすれば、返品が1つしかない場合、ソフトウェアがいつルーチンを終了するかについて推論する方が簡単です。これは例外に対する議論でもあります。
通常、早期回復アプローチを最小限に抑えます。
個人的には、最初にパス/フェイル状態チェックを行うことを好みます。これにより、関数の上部にある最も一般的な障害のほとんどを、続く残りのロジックとグループ化することができます。
場合によります。
すぐにチェックするためにいくつかの明らかな行き止まりの状態がある場合、早期戻り、機能の残りを無意味に実行することができます。*
関数がより複雑で、それ以外の場合は複数の出口ポイントがある可能性がある場合、retval +シングルリターンを設定します(読みやすさの問題)。
*これは、多くの場合、設計上の問題を示すことがあります。残りのコードを実行する前に、多くのメソッドが外部/パラメーターの状態などをチェックする必要があることがわかった場合、それはおそらく発信者が処理する必要があるものです。
ifを使用します
Gotoの本についてのDon Knuthの本では、彼が常に最も可能性の高い条件をIFステートメントで最初に来る理由を与えています。これはまだ合理的なアイデアであるという仮定の下で(そして、時代の速度について純粋な考慮事項からではありません)。早期のリターンは、特にエラー処理に使用されないことが多いという事実を考えると、コードが失敗する可能性が高い場合を除き、エラー処理に使用されないという事実を考えると、早期の収益は良いプログラミングの練習ではありません:-)
上記のアドバイスに従う場合、その返品を関数の下部に置く必要があります。その後、そこに戻りと呼ばれることさえできない場合があります。エラーコードを設定して2行を返すだけです。これにより、1エントリ1出口の理想を実現します。
Delphi特異的...
私はこれがDelphiプログラマーにとって良いプログラミングプラクティスであることを心に留めていますが、私には証拠はありません。 2009年以前、値を返す原子的方法はありません、私たちは持っています exit;
と result := foo;
または、例外をスローすることもできます。
代用しなければならなかった場合
if (true) {
return foo;
}
為に
if true then
begin
result := foo;
exit;
end;
あなたはあなたの機能のすべての一番上でそれを見るのにうんざりするかもしれません、そして
if false then
begin
result := bar;
...
end
else
result := foo;
そして、ただ避けてください exit
全体的に。
次の声明に同意します。
私は個人的には、機能のインデントを減らすため、ガード条項(2番目の例)のファンです。一部の人々は、関数から複数のリターンポイントをもたらすため、それらを好まない人もいますが、私は彼らにとって明確だと思います。
から取られた StackoverFlowでのこの質問.
私は最近、ほぼ独占的に早期リターンを極端に使用しています。私はこれを書きます
self = [super init];
if (self != nil)
{
// your code here
}
return self;
なので
self = [super init];
if (!self)
return;
// your code here
return self;
しかし、それは本当に問題ではありません。機能に複数のレベルのネストがある場合、それらはバストアップする必要があります。
書きたい:
if(someCondition)
{
SomeFunction();
}
あなたのように、私は通常最初のものを書きますが、最後のものを好みます。ネストされたチェックがたくさんある場合、通常、2番目の方法にはリファクタリングします。
エラー処理がチェックからどのように移動されるかは気に入らない。
if not error A
if not error B
if not error C
// do something
else handle error C
else handle error B
else handle error A
私はこれが好きです:
if error A
handle error A; return
if error B
handle error B; return
if error C
handle error C; return
// do something
上部の条件は「前処理」と呼ばれます。置くことによって if(!precond) return;
, 、すべての前提条件を視覚的にリストしています。
大きな「if-else」ブロックを使用すると、インデントオーバーヘッドが増加する可能性があります(3レベルのインデントについての引用を忘れてしまいました)。
声明が小さい場合は、保持することを好みます。
だから、間の選択:
if condition:
line1
line2
...
line-n
と
if not condition: return
line1
line2
...
line-n
私はあなたが「アーリーリターン」として説明したものを選択します。
気をつけてください、私は早期のリターンなどを気にしません。
ネストされている場合はネストされています。 最悪, 、どんな犠牲を払っても避けてください。
他の人が言うように、それは依存します。値を返す関数がほとんどない場合、早期リターンをコーディングする場合があります。しかし、かなりの機能のために、私は常にコードの場所を持っているのが好きです。そこでは、戻る前に実行されるものを置くことができることがわかります。
関数レベルでフェイルファーストを練習します。それはコードを一貫してきれいに保ちます(私にとって、そして私が一緒に働いたものにとって)。そのため、私はいつも早めに戻ります。
一部のチェック状態では、AOPを使用する場合、それらのチェックの側面を実装できます。