web-dev-qa-db-ja.com

ELSEの悪いプログラミングを使用していますか?

ELSE構文を使用することによって引き起こされたバグに遭遇することがよくあります。代表的な例は次のようなものです。

If (passwordCheck() == false){
    displayMessage();
}else{
    letThemIn();
}

私にとってこれはセキュリティの問題を叫んでいます。私はpasswordCheckがブール値である可能性が高いことを知っていますが、アプリケーションのセキュリティをその上に置くことはしません。文字列、intなどの場合はどうなりますか?

私は通常、ELSEの使用を避け、代わりに2つの完全に独立したIFステートメントを選択して、期待どおりのものをテストします。それ以外はすべて無視されるORは特に処理されます。

確かに、これはアプリに入るバグやセキュリティの問題を防ぐためのより良い方法です。

どうやってやるの?

18
billy.bob

elseブロックは、常にデフォルトの動作を希望するもので構成する必要があります。

それらを回避する必要はありません。適切に使用するように注意してください。

あなたの例では、デフォルトの状態はアクセスを許可しないことです。少しリファクタリングすると、次のことが可能になります。

If (passwordCheck)
{
   letThemIn();
}
else
{
   displayMessage();
}

つまり、パスワードチェックが機能する場合は許可し、そうでない場合はエラーメッセージを表示することは常に有効です。

もちろん、ifステートメントを完全に分離するのではなく、else ifを使用してロジックに追加のチェックを追加できます。

90
RYFN

いいえ、ELSEに問題はありません。 ELSEは新しいGOTOではありません。実際、IFの代わりに2つのELSEsを使用すると、いくつかの問題が発生する可能性があります。

例1:

if (this(is(a(very())==complex(check())))) {
   doSomething();
}

if (!this(is(a(very())==complex(check())))) {
   doTheOtherThing();
}

あなたはコピーペーストを見ますか?片方を変えてもう片方を忘れる日を待つだけです。

例2:

foreach(x in y) {
  if (first_time) {
    first_time = false;
    doSomething();
  }

  if (!first_time) {
    doTheOtherThing();
  }
}

ご覧のとおり、条件がすでに変更されているため、2番目のIFも最初のアイテムに対して実行されます。実際のプログラムでは、そのようなバグを見つけるのは困難です。

57
user281377

ELSEは常に存在します。あなたが書くなら

if(foo)
  bar();

あなたは実際に書きます

if(foo)
{
  bar();
}
else
{
   // do nothing
}

ELSEパスに入れるものはすべてあなたの責任です。

18

私は個人的にはelseをできるだけ避ける傾向がありますが、securityの問題には当てはまりません。

コードを読み取るとき、ネストされたステートメントはロジックに従うのを難しくします。これは、どの条件セットがそこにつながるかを覚えておく必要があるためです。このため、私は早期終了の大ファンです。

if (checkPassword != OK) { displayMessage(); return; }

letThemIn();

これは、インデントのレベルを回避する場合はいつでもforwhileを使用するcontinueおよびbreakループにも適用されます。

Chris Lattnerは LLVM Coding Standards で私よりも優れていると言っています。

14
Matthieu M.

次に、それらを交換します。

If (passwordCheck == true)
{
     letThemIn();
}
else
{
     displayMessage();
}
5
Ahmet Kakıcı

Matthieu M.と同様に、深くネストされたelseブロックよりも早期に終了することを好みます...これは十分に防御的なプログラミングを示します(条件が悪い場合、続行するポイントはありません)。多くの人が私たちに反対し、独自の出口を好みます。それは議論のポイントではありません(私は思う)。

今、私は確かに、特に単純で短い代替案について、意味があるときにelseを使用しています。前述のように、テストの複製は時間の浪費(プログラマとCPUの時間)であり、混乱の原因であり、バグ(一方が変更されたときに他方が変更された場合)です。
たまに、else部分にコメントを追加して、条件(特にif部分が長い場合(たとえば、レガシーコード))を思い出させます。代替。

関数型プログラミングの極端な支持者の中には、パターンマッチングを優先してifを完全に取り除くことを提案していることに注意してください...私の好みには少し極端すぎます。 :-)

3
PhiLho

ELSEの使用には何の問題もありません。ただし、コードが過度に複雑になり、読み取りや理解が困難になる可能性があります。設計が悪い可能性があります。それは確かにテストする必要がある追加のユースケースを示しています。

可能な場合はELSEを削除してみてください。ただし、それについて偏執狂しないでください。 Steve McConnellは、この完全なコードをコードコンプリートで呼びます。つまりコードには簡単な明確なパスがあります。

特定の問題を試すためのアプローチ:

  • ポリモーフィズムを使用します。システムの境界で、ユーザーのセキュリティ資格情報を検証します。それらが正当なものである場合は、システムの関連部分へのアクセス権を持つセッションオブジェクトを返すか、例外をスローします。ただし、これによりシステムがより複雑になる可能性があります。だから、あなたは何を理解して維持するのがより簡単であるかを決定します。

一般に、以下はコード内のELSEを削減するのに役立ちます。

  • 適切な要件があれば、コードでのそのような決定の必要性を減らすことができます。ユースケース(else)を実装する必要がない場合もあります。
  • より明確なデザイン。最大の凝集力と最小のカップリング。これにより、コンポーネントが他のコンポーネントで行われた決定と重複しないことが保証されます。
  • エラーケースを管理するための例外処理。
  • ポリモーフィズム(上記の例を参照)。
  • switchステートメント-これらは栄光のELSEですが、特定の状況ではより優れています。
2
Conor

コードがセキュリティリークであるというあなたの想定は、本当かもしれないし、そうでないかもしれません言語によって異なります使用しています。 Cコードでは問題になる可能性があります(特にCではブール値がゼロまたはゼロでないintであるため)-ただし、passwordCheck変数がブール値として宣言されている場合、最も強く型付けされた言語(つまり、ランタイム型チェック)では、それに何かを割り当てる方法はありません。実際、if述語のすべては、ブール演算子を使用する場合でも、単に値を使用する場合でも、ブール値に解決される必要があります。別のタイプのオブジェクトをpasswordCheckにバインドした場合、ランタイムは何らかのタイプの不正なキャスト例外をスローします。

単純なif/elseコンストラクトは、if/ifコンストラクトよりもはるかに読みやすく、誰かがコンストラクトを反転させようとした場合に不注意な問題が発生する可能性が低くなります。同じ例を少し見てみましょう。

if(passwordCheck == false) {
    denyAccess();
}

if(passwordCheck) {
    letThemIn();
}

上記で実行する相互に排他的な句の意味は失われます。それがif/else構造が伝えるものです。相互に排他的な2つの実行ブランチ。1つは常に実行されます。これはセキュリティの重要な部分です。letThemInを呼び出した後はdenyAccessにアクセスする方法がないことを確認してください。

コードを明確にするため、およびクリティカルセクションが最も保護されるようにするために、それらはプライマリ句(if部分)内にある必要があります。デフォルトの非準拠動作は、代替句(else部分)にある必要があります。例えば:

if(passwordCheck) {
    letThemIn();
} else {
    denyAccess();
}

注:さまざまな言語で作業する際に、「文字列の場合はどうなるのか」という質問を回避できるコーディングウサギを開発しました。基本的には、ブール式の最初に定数を置くことです。たとえば、passwordCheck == falseをチェックする代わりに、false == passwordCheckをチェックしています。これにより、C++で起こり得る偶発的な割り当ての問題も回避されます。このアプローチを使用すると、=の代わりに==と入力すると、コンパイラーは文句を言うでしょう。 JavaおよびC#のような言語では、コンパイラはif句の割り当てをエラーとして扱いますが、C++はそれを喜んで受け入れます。そのため、最初にnullを使用してnullチェックを行う傾向があります。

言語を定期的に変更する場合は、定数を最初に配置すると非常に役立ちます。ただし、私のチームではコーディング標準の反対であり、コンパイラーはとにかくこれらの問題をキャッチします。壊れるのは難しいかもしれません。

2
Berin Loritsch

プログラミングが悪いときにelseを使用すると言うのは、話すときにotherwiseを使うのが悪いと言うようなものです。

確かに、どちらも悪用される可能性がありますが、それは、偶然にそれらを含めて間違えたからといって回避されるべきだという意味ではありません。多くのバグがdefaultステートメントのswitchケースの欠落に依存していたとしても、私は驚かないでしょう。

1
gablin

Elseは、アプリケーションフローのホワイトリストと考えることができます。アプリケーションフローの続行を許可する必要がある(SHOULD)条件を確認します。条件が満たされない場合は、Elseを実行して、問題を解決するか、アプリケーションの実行を中止するか、または同様のものを実行します。

Else自体は悪くありませんが、使用方法を誤ると、望ましくない影響が生じることがあります。

また、あなたの声明について

"passwordCheckがブール値である可能性が高いことは知っていますが、アプリケーションのセキュリティを設定しません。"

開発するメソッドの場合、常に1つのデータ型を返します。 PHP Coreには2つ以上のデータ型を返すコードが散らばっていますが、関数呼び出しを推測するため、これは悪い習慣です。複数のデータ型を返す必要がある場合は、例外のスローを検討してください(これが別のデータ型を返す必要がある理由であることがよくあります-何かがひどく、ひどく間違っていた)、または1つのデータ型のみを返すことができるようにコードを再構築することを検討してください。

1
Craige

まず第一に。笑!他に回避する理由はまったくありません。形や形は決して悪いことではありません。

どちらかと言えばコードは

if(!IsLoggedIn) { ShowBadLoginOrNoAccessPage(); return }

そこには2つのifはなく、それ以外にはありません。これは、例外をスローするものを除いて、すべてのアプリで私が行うことです。例外は、表示する適切なページのURLをチェックする私の関数でキャッチされます(または、asp.netエラー関数でキャッチ/チェックを置くことができます)。許可しない、または例外で使用するメッセージを含む一般的なページを出力します(例外の種類を常に確認し、httpステータスコードを設定します)。

-編集-ammoQの例2に示すように、ifsはばかげています。 本当にelseはifと同じかそれ以上に優れています。コードパスが増えるとバグが発生する可能性が高くなると言われているので、ifsは避けなければなりません(ただし、個人的には行いません。ただし、returnとbreakを使用しています)。 Cyclomatic Complexity を参照してください

-編集2- if/elseの使用が心配な場合。また、私の好みは、次のような最短のコードブロックを上に置くことです。

if(cond == false) {
    a()
    b()
    c()
    onetwothree()
}
else
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}

むしろその後

if(cond) 
{
    a()
    b()
    c()
    more()
    onetwothree()
    code()
    longer()
}
else
{
    a()
    b()
    c()
    onetwothree()
}
1
user2528

可能な場合は、条件文の前にデフォルトを設定したい。私はそれが少し読みやすく、もう少し明示的であるように感じますが、これは単なる好みです。私は自分のコードで否定的な条件を試して回避する傾向があります。私は!fooまたはfalse == fooのチェックの大ファンではないので、他の条件は否定の条件に相当するもののように感じます。

foo = bar;

if ('fubar' == baz) {
    foo = baz;
}

の代わりに ...

if ('fubar' == baz) {
    foo = baz;
} else {
    foo = bar;
}

前のコードブロックは、少し読みやすくなっています。私のコードについて一種の懐疑的なパラノイアを持つことは私にとってより自然なようです。条件に関係なくデフォルトを設定すると、快適に感じる:P

0
Cory Comer

あらゆる種類の分岐ロジックを使用することは、可能な限り避けるべきだと私は主張します。 ELSEやIFには何の問題もありませんが、分岐ロジックを使用する必要性を最小限に抑えるためのコードを書く方法はたくさんあります。分岐ロジックを完全になくすことができるとは言っていません-いくつかの場所で必要になるでしょう-しかし、コードのリファクタリングを行って、適切なチャンクを取り除くことは可能です。ほとんどの場合、これによりコードの明瞭度と精度が向上します。

一例として、通常、三項演算子も適切な候補です。

If VIP Then 
  Discount = .25
Else
  Discount = 0
End If
Total = (1 - Discount) * Total

三元アプローチを使用する:

Discount = VIP ? .25 : 0
Total = (1 - Discount) * Total

三項演算子は、適切な方法で分岐を右にシフトします。

0
Mario T. Lanza