web-dev-qa-db-ja.com

try catch-blocksをうまく利用していますか?

私はいつもこれに取り組んでいます... try/catchingと、タブ、ブラケット、およびホットポテトのようにコールスタックにスローされる例外のこの厄介な混乱にならないコードとの適切なバランスを見つけようとしています。たとえば、SQLiteを使用して現在開発中のアプリがあります。 SQLite呼び出しを抽象化するデータベースインターフェースと、データベースに出入りするものを受け入れるモデルがあります。SQLite例外が発生した場合は、モデル(それを呼び出した人)に渡される必要があります。 )、誰がAddRecord/DeleteRecord/whateverを呼び出した人にそれを渡さなければならないか...

エラーコードは無視したり、忘れたりすることができるため、エラーコードを返すのではなく、例外のファンです。一方、例外は本質的に処理する必要があります(許可、I couldキャッチしてすぐに移動) ...)私が今行っている方法よりも優れた方法があるはずだと確信しています。

編集: 私はこれを少し違った言い方にするべきだった。私は別のタイプなどとして再スローすることを理解しています。私の質問は...そうするときにコードをクリーンに保つにはどうすればよいですか?しばらくすると、とても乱雑に感じ始めます。

15
trycatch

厳密に型指定された言語を使用していない場合でも、厳密な型指定の観点から考えてください。メソッドできないが期待する型を返す場合は、例外がスローされるはずです。

また、SQLExceptionをモデル(またはさらに悪い場合はUI)までスローするのではなく、各レイヤーは既知の例外をキャッチし、それらをそのレイヤーに適した例外にラップ/変更/置換する必要があります。

Layer      Handles Exception
----------------------------
UI         DataNotFoundException
Model      DatabaseRetrievalException
DAO        SQLException

これは、各レイヤーで探している例外の数を制限し、組織化された例外システムを維持するのに役立ちます。

14
Nicole

例外は、大部分が通常のケースを処理するため、よりクリーンなコードを書くことができ、例外的なケースは、異なるコンテキストでも後で処理できます。

例外を処理する(キャッチする)規則は、実際に何かを実行できるコンテキストによって実行する必要があるということです。しかし、それには例外があります:

例外はモジュールの境界(特にレイヤーの境界)でキャッチする必要があり、それらを囲むだけで、呼び出し側に意味のあるより高いレベルの例外をスローする必要があります。各モジュールとレイヤーは、実装の詳細を非表示にする必要があります例外(ヒープモジュールはHeapFullをスローすることがありますが、ArrayIndexOutOfBoundsはスローしません)。

あなたの例では、上位層がSQLite例外について何でもできるとは考えられません(もしそうなら、それはすべてSQLiteに結合されているため、データ層を別のものに切り替えることができません)。 Add/Delete/Updateなどが失敗する理由はいくつかありますが、それらの一部(並行トランザクションでの互換性のない変更)は、データ/永続性レイヤー(リカバリー規則の違反、f.i。)からでも回復できません。永続層は、例外をモデル層の用語で意味のあるものに変換する必要があります。これにより、上位層が再試行するか、正常に失敗するかを決定できます。

9
Apalala

一般的なルールとして、特定の例外(例:IOException)のみをキャッチし、例外をキャッチした後に特定の何かを行う必要がある場合に限ります。

それ以外の場合は、多くの場合、例外を表面に浮上させて、例外を公開して処理できるようにするのが最善です。一部の人々はこれをフェイルファストと呼んでいます。

アプリケーションのルートには、下からバブリングした未処理の例外をキャッチするための何らかのハンドラーが必要です。これにより、適切な方法で例外を提示、報告、または管理する機会が得られます。

例外のラップは、分散システム全体で例外をスローする必要があり、クライアントにサーバー側の障害の定義がない場合に役立ちます。

5
Ed James

あなたがスタッククラスを書いていると想像してください。クラスに例外処理コードを配置しないでください。その結果、次の例外が発生する可能性があります。

  1. ArrayIndexError-ユーザーが空のスタックからポップしようとすると発生します
  2. NullPtrException-null参照を参照しようとする実装のバグが原因で発生します

例外をラップする単純化したアプローチでは、これらの例外を両方ともStackError例外クラスでラップすることを決定する場合があります。ただし、これは例外をラップするポイントを本当に逃してしまいます。オブジェクトが低レベルの例外をスローする場合は、オブジェクトが壊れていることを意味します。ただし、これが許容されるケースが1つあります。オブジェクトが実際に壊れている場合です。

例外をラップするポイントは、オブジェクトが通常のエラーに対して適切な例外を提供することです。空のスタックからポップすると、スタックはArrayIndexErrorではなくStackEmptyを発生させるはずです。意図はnotであり、オブジェクトまたはコードが壊れている場合に他の例外がスローされないようにします。

私たちが本当に避けたいのは、高レベルのオブジェクトを介して渡された低レベルの例外をキャッチすることです。空のスタックからポップしたときにArrayIndexErrorをスローするスタッククラスは、小さな問題です。実際にそのArrayIndexErrorをキャッチすると、深刻な問題が発生します。低レベルのエラーの伝播は、それらをキャッチするよりもはるかに深刻な罪ではありません。

これをSQLExceptionの例に戻すには、なぜSQLExceptionsを取得するのですか? 1つの理由は、無効なクエリを渡すためです。ただし、データアクセスレイヤーが不適切なクエリを生成している場合は、壊れています。 DataAccessFailure例外でその破損を再ラップしようとするべきではありません。

ただし、データベースへの接続が失われたためにSQLExceptionが発生することもあります。その時点での私の戦略は、最後の防御線で例外をキャッチし、データベース接続が失われ、シャットダウンされたことをユーザーに報告することです。アプリケーションがデータベースにアクセスできなくなったため、実行できることはこれ以上ありません。

私はあなたのコードがどのように見えるのかわかりません。しかし、すべての例外を盲目的に、より高いレベルの例外に変換しているように見えるかもしれません。比較的少数のケースでのみそれを行うべきです。最も低いレベルの例外は、コードのバグを示します。それらをキャッチして再ラップすることは逆効果です。

4
Winston Ewert