すべての条件をキャッチするはずのif-elseラダー-冗長なfinal句を追加する必要がありますか?


10

これは私が最近よくしていることです。

例:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else if (i > current) {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

多くの場合、if / elseラダーはこれよりもはるかに複雑です...

最後の条項をご覧ください。それは冗長です。はしごは最終的にすべての可能な条件をキャッチすることになっています。したがって、次のように書き直すことができます。

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

これまでコードを書いていた方法ですが、このスタイルは嫌いです。私の不満は、コードの最後の部分が実行される条件がコードから明らかでないということです。したがって、私はこの状態をより明確にするために明示的に書き始めました。

しかしながら:

  • 最終的な完全な状態を明示的に書くことは私自身のアイデアであり、私は自分のアイデアで悪い経験をしています-通常、私がやっていることがどれほど恐ろしいかについて人々は私に悲鳴を上げます次善;
  • これが悪い考えである理由の1つのヒント:JavaScriptには適用できませんが、他の言語では、コンパイラーは関数の終わりに到達するコントロールについて警告またはエラーを発行する傾向があります。そのようなことをすることのヒントはあまり人気ではないかもしれません、または私はそれを間違っています。
    • コンパイラの不満からコメントに最終的な条件が書かれることがありましたが、コードとは異なり、コメントは実際のプログラムのセマンティクスに影響を与えないので、そうすることは恐ろしいことだと思います。
    } else { // i > current
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }

何か不足していますか?それとも、私が説明したことをしても大丈夫ですか、それとも悪い考えですか?


コードの冗長性が欠陥の可能性を高めるという@Christopheの優れた答えの+1。また、はるかに効率の低い問題もあります。さらに詳しく言えば、問題のコード例については、一連のif-else-ifをelseなしの別個のifとして書くことができますが、if-elseは読者に排他的な代替の概念を提供するため、通常はそうしません。一連の独立した条件ではなく(1つの一致後でもすべての条件を評価する必要があることを示しているため、効率も低下します)。
Erik Eidt

@ErikEidt>これは、関数から戻るか、ループから「ブレーク」している場合(上記の例ではそうではない)を除いて、+ 1に一致した後でもすべての条件が評価される必要があることを示しています。
DarekNędza19年

1
+1、いい質問です。余談ですが、NaNが存在する場合、例が完全ではないことに興味を持つかもしれません。
モノセル

回答:


6

どちらのアプローチも有効です。しかし、長所と短所を詳しく見てみましょう。

このifような些細な条件を持つチェーンの場合、それは本当に重要ではありません:

  • final elseを使用すると、elseがどの条件でトリガーされるかを読者が知るのは明らかです。
  • final else ifを使用すると、elseすべてをカバーしたので、追加が不要であることは読者にとって明らかです。

ただし、ifより複雑な条件に依存する多数のチェーンがあり、複数の変数の状態を、おそらく複雑な論理式と組み合わせます。この場合、それほど明白ではありません。そして、ここで各スタイルの結果:

  • final else:分岐の1つが確実に行われます。1つのケースを忘れた場合は、最後のブランチを通過するため、デバッグ中に、最後のブランチが選択されていて、別のブランチを予期している場合は、すぐにわかります。
  • final else if:コードに冗長な条件を導出する必要があります。これにより、すべてのケースをカバーしないというリスクを伴う潜在的なエラーソースが作成されます。さらに、ケースを逃した場合、何も実行されず、何かが欠落していることを見つけるのがより困難になる可能性があります(たとえば、設定することが予想された一部の変数が以前の反復の値を保持している場合)。

したがって、最後の冗長な状態がリスクの原因になります。これが、決勝に行くことをむしろ勧める理由elseです。

編集:高信頼性コーディング

高い信頼性を念頭に置いて開発している場合は、別のバリアントに興味があるかもしれません。冗長な明示的なファイナルelse ifをファイナルで完了してelse、予期しない状況をキャッチします。

これは防御コーディングです。これは、SEI CERTMISRAなどの一部のセキュリティ仕様で推奨されています。一部の静的分析ツールは、これを体系的にチェックされるルールとして実装しています(これはコンパイラの警告を説明する可能性があります)。


7
最後の冗長条件の後で、「あなたは私に届くはずではなかった!」という例外を常にスローするelseを追加するとどうなるでしょうか。-それは私のアプローチの問題のいくつかを軽減しますか?
gaazkam

3
@gaazkamはい、これはコーディングの非常に防御的なスタイルです。したがって、最終的な条件を計算する必要がありますが、エラーが発生しやすい複雑なチェーンの場合は、少なくともすばやく見つけることができます。この亜種で私が目にする唯一の問題は、明らかなケースでは少しやり過ぎであるということです。
Christophe

@gaazkamコメントに記載されているあなたの追加のアイデアにも対処するために私の回答を編集しました
Christophe

1
@gaazkam例外を投げるのは良いことです。その場合は、メッセージに予期しない値を含めることを忘れないでください。再現が困難な場合があり、予期しない値が問題の原因についての手掛かりを提供する場合があります。
Martin Maat

1
別のオプションはassert、最後にを置くことelse ifです。ただし、スタイルガイドは、それが良いアイデアかどうかによって異なる場合があります。(assertプログラマーが台無しにしない限り、anの条件は決して偽りであってはなりません。そのため、これは少なくともその機能をその目的に使用しています。しかし、多くの人々がそれを誤用して、多くのショップが完全にそれを禁止しています。)
Kevin

5

これまでの回答に欠けているのは、どのような失敗が害が少ないかという問題です。

ロジックが適切であれば、何をしてもかまいません。重要なケースは、バグがある場合に何が起こるかです。

最後の条件を省略します。それが正しくない場合でも、最後のオプションが実行されます。

あなたは単に最終的な条件を追加します:状況に応じて、これは単に何かが表示されないことを意味するかもしれません(害が少ない)、または後の時点でnull参照例外を意味するかもしれません(デバッグであるかもしれません)疼痛。)

最後の条件と例外を追加します:スローします。

これらのオプションのどれが最適かを判断する必要があります。開発コードでは、これは非常に簡単であると私は考えています。ただし、スローする前に、circle.srcをエラー画像に設定し、circle.altをエラーメッセージに設定します。後で誰かがアサーションをオフにすると、無害に失敗します。

考慮すべきもう1つのこと-回復オプションは何ですか?回復パスがない場合もあります。これの究極の例は、アリアンVロケットの最初の打ち上げだと思います。キャッチされていない/ 0(実際には除算オーバーフロー)エラーが発生し、ブースターが破壊されました。実際には、クラッシュしたコードはその時点ではまったく意味がありませんでした。軌道またはブームになったら、できる限り最善を尽くします。エラーは許されません。(これによりロケットが迷った場合、射程安全担当者が鍵を回します。)


4

私がお勧めするのはassert、次の2つのスタイルのいずれかで、最終的なelseにステートメントを使用することです。

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else {
        assert i > current
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    }
}

またはデッドコードアサーション:

setCircle(circle, i, { current }) {
    if (i == current) {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
    } else if (i < current) {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
    } else if (i > current) {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
    } else {
        assert False, "Unreachable code"
    }
}

多くの場合、コードカバレッジツールは、カバレッジレポートから "falseをアサート"のようなコードを無視するように構成できます。


条件をアサーションに入れると、ブランチの条件を効果的に明示的に文書化できますが、コメントとは異なり、アサーション条件は実際にチェックでき、開発中または本番環境でアサーションを有効にしておくと失敗します(アサーションを有効にしておくことをお勧めしますパフォーマンスにあまり影響を与えない場合は、本番環境で)。


1
私はあなたの最初のオプションが好きではありません、2番目はそれが不可能であるべきケースであることをはるかに明確です。
Loren Pechtel

0

条件を評価する「アサート」されたマクロを定義しました。デバッグビルドでは、デバッガーに分類されます。

したがって、3つの条件のうちの1つが真でなければならないことを100%確信している場合は、

If condition 1 ...
Else if condition 2 .,,
Else if asserted (condition3) ...

これにより、1つの条件が真であることが明確になり、アサート用の追加の分岐は必要ありません。


-2

それ以外は完全に避けることをお勧めしますifコードのブロックが処理することになっているものを宣言するために使用し、関数を終了することによってブロックを終了します。

これにより、非常に明確なコードが生成されます。

setCircle(circle, i, { current })
{
    if (i == current)
    {
        circle.src = 'images/25CE.svg'
        circle.alt = 'Now picking'
        return
    }
    if (i < current)
    {
        circle.src = 'images/25C9.svg'
        circle.alt = 'Pick failed'
        return
    }
    if (i > current)
    {
        circle.src = 'images/25CB.svg'
        circle.alt = 'Pick chance'
        return
    }
    throw new Exception("Condition not handled.");
}

最後ifはもちろん冗長です...今日。今後の開発者がブロックを再配置する場合は、非常に重要な考えになる可能性があります。そのままにしておくと便利です。


1
最初のブランチを実行すると2番目のテストの結果が変わるかどうかを検討する必要があるため、実際には非常に不明確です。プラス無意味な複数のリターン。さらに、条件が真ではない可能性もあります。
gnasher729

意図的に複数のケースを取ることができると期待しているときに、このようなステートメントを実際に書いています。これは、フォールスルーを許可しないswitchステートメントを持つ言語で特に役立ちます。選択肢が相互に排他的である場合は、ケースが相互に排他的であること(elseを使用)が明らかであるように記述する必要があります。そして、そのように書かれていない場合は、ケースが相互に排他的ではないことを意味します(フォールスルーが可能です)。
ジェリーエレミア

@ gnasher729私はあなたの反応を完全に理解しています。最初は「他人を避けて」「早めに帰る」のに苦労したとても賢い人を知っています... これらのアイデアは、客観的かつ適度に複雑さを軽減するため、少し時間をかけてアイデアに慣れ、検討することをお勧めします。初期のリターンは、実際に最初のポイントに対処します(ブランチが別のテストを変更するかどうかを検討する必要があります)。そして「条件が真ではない可能性」は依然として存在します。このスタイルでは、隠れたロジックの欠陥ではなく、明らかな例外が発生します。
John Wu

しかし、両方のスタイルの循環的複雑度は正確に同じではありませんか?つまり、条件の数に等しい
gaazkam
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.