私はエンタープライズを設計する請負業者として働いていますJavaクライアント向けのアプリケーションをテクニカルリードの役割で使用します。このアプリケーションはエンドユーザーが使用し、アプリケーションをサポートするサポートチームがいます私たちが去るとき。
私が一緒に作業している他のテクニカルリードは、例外処理によってコードが汚くなるという印象を受けています。チェックされていない例外を処理する必要がないように、システムはチェックされた例外をサービス層からのみスローし、残りのコードは他のすべての層からランタイム例外をスローする必要があります。
ビジネスアプリケーションで未チェックの例外をスローする必要性は何ですか?
ランタイム例外に関する過去の私の経験から:
1)チェックされていない例外は、Javadocにも表示されないため、コードを予測不能にします。
2)ビジネスアプリケーションでチェックされていない例外をスローすることは、それをスローしてユーザーの顔に直接当てはまる場合、ユーザーにどのように説明するのですか? 500 -Internal Error. Contact Administrator
を表示するWebアプリケーションを十分に見ました。これは、エンドユーザーにとっても、アプリケーションを管理するサポートチームにとっても意味がありません。
3)ランタイム例外をスローすると、例外をスローするクラスのユーザーは、ソースコードをウォークスルーしてデバッグし、例外がスローされる理由を確認する必要があります。これは、ランタイム例外のJavadocがたまたま文書化されている場合にのみ回避できます。
チェックされた例外は、言語設計での失敗した試用です。彼らはあなたに非常に漏れやすい抽象化と汚いコードを強制します。それらはできるだけ避けられるべきです。
あなたのポイントについては:
1)チェックされた例外はコードをダーティにし、それらがeverywhereを表示するため、予測不能ではありません。
2)チェックされた例外がユーザーにどのように表示されるのが適切ですか?チェックされた例外とチェックされていない例外の唯一の実際の違いは技術的なものであり、ソースコードにのみ影響します。
3)スタックトレースについて聞いたことがありますか?例外がチェックされているかどうかに関係なく、例外がスローされた場所を正確に通知します。実際には、チェックされた例外はラップされることが多いため、デバッグに悪影響を与える傾向があり、長くて醜いスタックトレースが発生したり、ラップが誤って行われたために完全に失われたりします。
例外には2種類あります。「通常」発生し、通常は発生場所の非常に近くで処理される例外と、本当に例外的で一般的に非常に上位のレイヤーで処理できる例外(現在のアクションを中止してログに記録する/エラーを表示します)。
チェック例外は、例外が定義された時点で言語構文にこの違いを付けようとする試みでした。これに関する問題は
私の見解では、どのタイプの例外がスローされるかは、コードが何をしているかに依存します。
チェック例外を頻繁に発生する可能性がある場合(ユーザーが危険なデータを入力する場合など)にチェック例外をスローし、呼び出しコードが状況を処理することを期待します。
呼び出し側のコードで処理できないと思われるまれな状況が発生した場合、チェックされていない/実行時の例外(チェックされる頻度が低い)をスローします。一例は、私が発生することを決して予期しない、ある種の奇妙なメモリ関連のエラーかもしれません。オフになっているのは、アプリケーションを停止すると予想される種類のエラーです。
ある程度のサポートがなければ、ユーザーの前に例外が表示されることはありません。 「このエラーダンプを電子メールにカットアンドペーストしてください」の場合も同様です。エラーがあると言われるほどユーザーを煩わしくすることはありませんが、修正を開始するためにユーザーが実行できる詳細やアクションはありません。
私の推測では、あなたが言及している哲学は2つのソースのうちの1つから来ていると思います。
未チェックのランタイム例外が必要ない場合は、なぜJavaを使用しているのですか?ほぼすべての場所でランタイム例外が発生する可能性があります-特にNullPointerException、ArithmeticException、ArrayIndexOutOfBounds例外など。
また、SAP NetWeaverのインストールなど、一部のJ2EEシステムのログファイルを一度読み取ると、そのような例外が文字通り常に発生していることがわかります。
留意すべき例外処理のルールがいくつかあります。ただし、最初に、例外はコードによって公開されるインターフェースの一部であることを覚えておく必要があります。 それらを文書化します。もちろん、これはインターフェースがパブリックインターフェースの場合に特に重要ですが、プライベートインターフェースでも非常に良いアイデアです。
例外は、コードがそれらに対して理にかなった何かを実行できる時点でのみ処理されるべきです。最悪の処理オプションは、それらについて何もしないことです。これは、それが正確に正しいオプションである場合にのみ実行する必要があります。 (コードにそのような状況がある場合、空のボディを気にしないように知っているので、その旨のコメントを含めます。)
2番目の最悪のオプションは、元のものが原因として関連付けられていない、無関係な例外をスローすることです。ここでの問題は、問題の診断を可能にする元の例外内の情報が失われることです。あなたは誰も何もできないことを作成しています(「それがうまくいかない」と不平を言うことを除いて、そして私たちは皆嫌いな方法を知っていますそれらバグレポート)。
例外をログに記録する方がはるかに優れています。これにより、問題が何であるかを見つけて修正することができますが、例外をログに記録する必要があるのは、他の方法でそれが失われるか、外部接続で報告される場合だけです。それは、より頻繁にログを記録することがそのような大きな問題であるからではなく、過剰なログが、より多くの情報を含まずにログがより多くのスペースを消費することを意味するからです。例外をログに記録したら、précisを良心を持ってユーザー/クライアントに報告できます(そのレポートの生成(または他の相関識別子)を使用して、必要に応じて短いバージョンを詳細と一致させることができます)。
もちろん、最良のオプションは、例外を完全に処理し、エラー状況全体を処理することです。これができるなら、ぜひやってみてください!これは、例外をログに記録する必要がないことを意味する場合もあります。
例外を処理する1つの方法は、問題のより高いレベルの説明を提供する別の例外をスローすることです(たとえば、「failed to initialize
」の代わりに「index out of bounds
」)。例外の原因に関する情報を失わない限り、これは良いパターンです。詳細な例外を使用して、上位レベルの例外のcause
を初期化するまたは詳細をログに記録します(上記を参照)。 IPC呼び出しなど)プロセス間境界を越えようとする場合、ロギングが最も適切です。低レベルの例外クラスがすべてのクラスに存在するという保証はないためです。接続のもう一方の端です。内部境界を越えるときは、付随する原因として保持することが最も適切です。
もう1つのパターンは、キャッチアンドリリースです。
try {
// ...
} catch (FooException e) {
throw e;
}
これはアンチパターンです以外の場合他のcatch
句から型制約を取得しているため、単に例外を許可することはできませんそのまま通過します。それは、Javaの醜い機能です。
チェックされた例外とチェックされていない例外の間には、メソッドの境界を越えるチェックされた例外を宣言する必要があること以外に、実際の違いはありません。チェックされていない例外がコードによって故意にスローされていることがわかっている場合は、チェックされていない例外を(@throws
javadocコメントとともに)文書化することをお勧めします。意図的にJava.lang.Error
またはそのサブクラスをスローしないでください(JVM実装を作成している場合を除きます)。
Opinion:予期しないエラーケースは常にコードのバグを表します。チェックされた例外はこの脅威を管理する方法であり、開発者がエラーのケースを処理するトラブルを回避する方法としてチェックされていない例外を意図的に使用している場合、いくつかの時間をクリーンアップする必要がある多くの技術的負債を積み上げています堅牢なコードが必要な場合。ずさんなエラー処理は専門的ではありません(そして、エラー処理を調べることは、プログラマーが本当にどれほど優れているかを判断するための良い方法です)。
アプリケーションが回復できるときは、チェックされた例外のみをスローするべきだと思います。したがって、ライブラリのユーザーはこれらの例外をキャッチして、回復するために必要なことを行う必要があります。それ以外のものはチェックしないでください。
例として、
アプリがファイルまたはデータベースからデータを読み込む場合。それから.
try {
File data = new File(...);
// parse file here
} catch (Exception ex) {
throw new MissingDataFileException("data file not found");
}
呼び出し側は常にチェックされたMissingDataFileExceptionをキャッチして、データベースからデータをロードしようとすることができます。
try {
Connection con = DriverManager.getConnection( Host, username, password );
// query data here
} catch (Exception ex) {
throw new RuntimeException("better call Saul!");
}