私は過去3年間プログラミングをしています。私がプログラミングするときは、既知のすべての例外を処理し、ユーザーに適切に警告します。最近、ほぼすべてのメソッドが try/catch ブロック内にラップされているコードを見たことがあります。著者は、これは防御的プログラミングの一部であると述べています。これは本当に防御的なプログラミングなのだろうか?すべてのコードを try ブロックに入れることをお勧めしますか?
12 に答える
私の基本的なルールは次のとおりです。例外の原因となった問題を修正できない限り、それをキャッチせず、対処できるレベルまでバブルさせます。
私の経験では、すべてのcatchブロックの95%は、例外(catch {}
)を無視するか、単にエラーをログに記録して例外を再スローします。後者は正しいことのように思えるかもしれませんが、実際には、これをすべてのレベルで実行すると、同じエラーメッセージの5つのコピーでログが乱雑になるだけです。通常、これらのアプリの最上位レベルには「キャッチを無視」があり(「すべての下位レベルで試行/キャッチがあるため」)、例外が多く発生する非常に低速なアプリになり、エラーログが長すぎます。誰でも喜んでそれを見ていきます。
Try ... Catchの広範な使用は、防御的なプログラミングではなく、死体を直立した位置に釘付けにするだけです。
試してみてください...最後に、予期しない例外が発生した場合の回復に幅広く使用できます。例外が予想され、その対処方法がわかった場合にのみ、代わりにTry..Catchを使用する必要があります。
Try..Catch System.Exceptionが表示されることがあります。この場合、catchブロックは例外をログに記録して再スローします。そのアプローチには少なくとも3つの問題があります。
- Rethrowは未処理の例外を想定しているため、プログラムは不明な状態にあるため終了する必要があります。ただし、catchを使用すると、Catchブロックの下のFinallyブロックが実行されます。未定義の状況では、これらのFinallyブロックのコードが問題を悪化させる可能性があります。
- これらのFinallyブロックのコードは、プログラムの状態を変更します。したがって、例外が最初にスローされたときの実際のプログラム状態は、ログに記録されません。そして、状態が変わったため、調査はより困難になります。
- デバッガーは元のスローではなく、再スローで停止するため、悲惨なデバッグエクスペリエンスが得られます。
いいえ、「防御的プログラミング」ではありません。あなたの同僚は、良い習慣の流行語を使って自分の悪い習慣を正当化しようとしています。
彼がしていることは、「敷物の下を掃除する」と呼ばれるべきです。(void)
メソッド呼び出しからのエラーステータスの戻り値を均一に処理するようなものです。
「防御的プログラミング」という用語は、エラー状況から回復できるように、またはエラー状況を完全に回避できるようにコードを記述することを表します。例えば:
private String name;
public void setName(String name) {
}
name == null をどのように処理しますか? 例外をスローしますか、それとも受け入れますか? 名前のないオブジェクトを持つ意味がない場合は、例外をスローする必要があります。name == "" はどうですか?
しかし...後でエディタを書きます。UI をセットアップしているときに、ユーザーが名前を削除することを決定したり、ユーザーが編集中に名前が空になったりする状況があることに気付きました。
もう一つの例:
public boolean isXXX (String s) {
}
ここでの防御戦略は、多くの場合、s == null の場合に false を返すことです (可能な場合は NPE を避けてください)。
または:
public String getName() {
}
防御的なプログラマーは、コードの呼び出しで NPE を回避するために、 name == null の場合に "" を返すことがあります。
ランダムな例外を処理する場合は、次の目的で、1 つの場所 (アプリケーションの最上部) でのみ処理します。
- ユーザーにフレンドリーなメッセージを表示し、
- 診断を保存します。
それ以外の場合は、できるだけ早く場所固有のクラッシュを発生させて、できるだけ早くキャッチできるようにする必要があります。そうしないと、例外処理がずさんな設計とコードを隠す方法になります。
例外が予測可能なほとんどの場合、例外ハンドラーがキャッチする条件について、事前にテストすることができます。
一般に、If...Else は Try...Catch よりも優れています。
ランダムな例外をキャッチするのは悪いことです。では、どうしますか?
- それらを無視しますか?素晴らしい。それが彼らにとってどのように機能するか教えてください。
- それらをログに記録して実行を続けますか?私はそうは思わない。
- クラッシュの一部として別の例外をスローしますか?それをデバッグして頑張ってください。
実際に意味のあることを実行できる例外をキャッチするのは良いことです。これらのケースは、簡単に識別および保守できます。
余談ですが、私の同僚の 1 人が、メソッドが実際にスローする例外の種類をリストするのではなく、"throws Exception" を使用してメソッド シグネチャを記述するたびに、私はそれらを調べて頭の中で撃ちたいと言うことができますか? 問題は、しばらくすると 14 レベルの呼び出しがすべて「例外をスローする」と表示されるようになるため、実際にスローするものを宣言するようにリファクタリングすることは大きな課題です。
「多すぎる」処理などがあり、すべての例外をキャッチすると、ポイントが無効になります。特にC++の場合、catch(...)ステートメントはすべての例外をキャッチしますが、例外のタイプがわからないため(そしてそれは何でもかまいません)、その例外の内容を処理することはできません。
完全にまたは部分的に処理できる例外をキャッチし、部分的な例外を再スローする必要があります。処理できない例外をキャッチするべきではありません。それは、後であなたを噛む可能性のある(またはむしろそうなる)エラーを難読化するだけだからです。
私はこの慣行に反対することをお勧めします。スローされる可能性のある例外のタイプがわかっている場合に、コードをtry-catchブロックに入れることは1つのことです。あなたが述べたように、それはあなたが優雅に回復すること、そして/またはエラーに関してユーザーに警告することを可能にします。ただし、発生する可能性のあるエラーがわからないようなブロック内にすべてのコードを配置することは、例外を使用してプログラムフローを制御することです。
適切に構造化されたコードを記述している場合は、発生する可能性のあるすべての例外について知っており、それらを具体的にキャッチできます。特定の例外がどのようにスローされるかわからない場合は、念のためにそれをキャッチしないでください。それが起こったとき、あなたはそれから例外とそれを引き起こしたものを理解し、そしてそれを捕まえることができます。
本当の答えは「状況次第」だと思います。try-catchブロックが非常に一般的な例外をキャッチしている場合、近所から車を運転することが防御的な運転であるのと同じように、防御的なプログラミングであると言えます。try-catch(imo)は、特定の例外に合わせて調整する必要があります。
繰り返しになりますが、これは私の意見ですが、防御プログラミングの私の概念は、必要なトライキャッチブロックの数が多い/多いのではなく、少ない/小さいということです。そもそも例外条件が存在しないようにするために、コードはできる限りのことをしている必要があります。
C++ で多くの try/catch ブロックを記述する理由の 1 つは、例外がスローされた場所のスタック トレースを取得することです。あなたがしていることは、どこにでもtry/catchを書き、(例外を処理するのに適切な場所にいないと仮定して)catchにトレース情報をログに記録させ、例外を再スローさせることです。このようにして、例外が完全にバブルアップしてプログラムが終了した場合、すべてがうまくいかなかった場所の完全なスタック トレースが得られます (これを行わないと、未処理の C++ 例外が発生します)。スタックをほどいて、それがどこから来たのかを突き止める可能性を根絶しました)。
より優れた例外処理を備えた言語 (つまり、キャッチされなかった例外がどこから来たのかを教えてくれる) では、例外に対して何かできる場合にのみ例外をキャッチしたいと思うでしょう。そうしないと、プログラムが読みにくくなるだけです。
特にリアルタイム (データベースへのアクセスなど) が使用される場合は、「try」「catch」ブロックが非常に役立つことがわかりました。
多すぎる?見る人の目。
ログを Word にコピーし、"find" を使用して検索すること (ログ リーダーに含まれているツールの一部として "find" または "search" がない場合) は、シンプルですが優れた方法であることがわかりました。詳細ログ。
確かに普通の意味で「防御的」に見えます。
経験を通じて、あなたのマネージャー、チームリーダー、または同僚がすることは何でも従うことがわかりました. 自分でプログラミングするだけの場合は、コードが「安定」するまで、またはデバッグビルドでそれらを使用し、完了したら削除してください。