18

要求されたレコードが存在しない場合、または呼び出し元が許可されていないためにアクセスできない場合に、多くの場所でnullを返すレガシーサービスレイヤーを使用するプロジェクトで作業しています。IDからリクエストされた特定のレコードについて話しています。たとえば、次のようなものです。

UserService.get(userId);

最近、このAPIを変更するか、代わりに例外をスローする新しいAPIを追加するようにプッシュしました。チェックされた例外とチェックされていない例外についての議論が続いています。

JPA / Hibernateなどの設計者からのメモをとって、チェックされていない例外が最も適切である可能性があることを提案しました。私の主張は、APIのユーザーがこれらの例外から回復することを合理的に期待することはできず、99%の場合、エラーが発生したことをアプリケーションユーザーに通知することができます。

ランタイム例外を一般的な処理メカニズムまで伝播させることで、エッジケースの例外の処理に伴う複雑さと必要なブランチ処理の多くが明らかに軽減されます。しかし、そのようなアプローチを取り巻く多くの懸念があります(当然そうです)。

JPA / EJBやHibernateなどのプロジェクトの設計者が、チェックされていない例外モデルを選択したのはなぜですか?それには非常に正当な理由がありますか?長所/短所は何ですか。これらのフレームワークを使用している開発者は、アダプターラッパーなどを使用して、スローされた場所の近くでランタイム例外を処理する必要がありますか?

これらの質問への回答が、私たち自身のサービスレイヤーに関する「正しい」決定を下すのに役立つことを願っています。

4

5 に答える 5

27

チェックされていない例外がより便利なAPIになるという見方には同意しますが、それが最も重要な利点ではありません。代わりにこれです:

チェックされていない例外をスローすると、重大なバグを回避するのに役立ちます。

これが理由です。チェックされた例外に対処することを余儀なくされた10人の開発者を考えると、それらに対処するための20の異なる戦略が得られますが、その多くは完全に不適切です。より一般的な悪いアプローチのいくつかを次に示します。

  • 飲み込む。例外をキャッチし、完全に無視します。アプリが不安定な状態になっているにもかかわらず、何も起こらなかったかのように続行します。
  • ログに記録して飲み込みます。例外をキャッチしてログに記録し、今は私たちが責任を負っていると考えます。その後、何も起こらなかったかのように続けます。
  • ミステリーのデフォルト。例外をキャッチし、通常はユーザーに通知せずに、フィールドをデフォルト値に設定します。たとえば、一部のユーザーの役割を読み込めない場合は、特権の低い役割を選択して割り当てます。ユーザーは何が起こっているのか疑問に思います。
  • 愚かな/危険な謎のデフォルト。例外をキャッチし、いくつかのフィールドをいくつかの本当に悪いデフォルト値に設定します。私が実際に見た1つの例:ユーザーの役割をロードできないので、先に進んで最善を尽くします(つまり、だれにも迷惑をかけないように、ユーザーに高い特権の役割を与えます)。
  • 誤報。開発者は例外が何を意味するのかわからないので、自分のアイデアを思いつくだけです。IOException接続の確立が問題とは関係がない場合でも、「サーバーに接続できません」になります。
  • 広範なキャッチを介してマスクし、誤って報告します。メソッドが実際にスローする2つのチェックされた例外の代わりに、Exceptionまたは(ugh)をキャッチして、コードをクリーンアップしてみてください。Throwable例外処理コードは、リソースの可用性の問題(たとえば、いくつかIOExceptionのs)と完全なコードエラー(たとえばNullPointerException)を区別しようとはしません。実際、例外の1つを任意に選択し、すべての例外をそのタイプであると誤って報告することがよくあります。
  • 膨大な試行を経てマスクし、誤って報告します。前の戦略の変形は、例外を宣言する呼び出しの全体を単一の大きなtryブロックのスコープに入れてから、スローされるすべての例外を処理するものが他にないために、いずれExceptionかをキャッチすることです。Throwable
  • 抽象化-不適切な再スロー。例外が抽象化に不適切な場合でも、例外を再スローします(たとえば、リソースを非表示にすることになっているサービスインターフェイスからリソース関連の例外を再スローします)。
  • ラッピングせずに投げ直します。例外を再スローします(チェックされていないか、抽象化に適したチェックがされています)が、ネストされた例外を削除するだけで、実際に何が起こっているのかを理解する機会が得られます。
  • 劇的な反応。JVMを終了して、致命的ではない例外に応答します。(これについては、このブログ投稿に感謝します。)

私の経験では、正しい応答を確認するよりも、上記のアプローチを確認する方がかなり一般的です。多くの開発者(「上級」開発者でさえ)は、たとえそれが不安定な状態でアプリを実行することを意味するとしても、例外を絶対に抑制しなければならないというこの考えを持っています。それは危険なほど間違っています。

チェックされていない例外は、この問題を回避するのに役立ちます。例外の処理方法を知らない開発者は、例外を克服するのに不便であると見なす傾向があり、例外をキャッチするために邪魔をすることはありません。したがって、例外は、スタックトレースを提供し、一貫した方法で処理できる最上位にバブルアップします。例外をバブルアップさせるよりも実際にやるべきことがあるというまれなケースでは、あなたを止めるものは何もありません。

于 2011-07-06T06:36:51.520 に答える
2

私は間違っている可能性がありますが、EJBコンテナが例外を処理する方法に関連している可能性があります。EJB例外処理のベストプラクティスから:

EJBコンテナの内部ハウスキーピングを使用するには、チェックされた例外をチェックされていない例外としてスローする必要があります。

于 2011-07-06T05:01:31.087 に答える
1

次の観点から、チェックされた/チェックされていない例外が表示されます(標準のJDKから手がかりを得ています)

  1. コード/ライブラリがJVMの外部にあるリソースに依存している場合は、チェックされた例外を使用します(たとえば、.file / socket / remote apiなど。つまり、JVMはそれを制御できません)。コードは、ソースが発生する可能性のあるすべてのエラー条件を処理する必要があります。これは、プログラムが堅牢であり、そのリソースに問題が発生した場合に正常に失敗することを確認するためです。

  2. チェックされていない例外は、重大なエラー状態、またはコード内の実際のバグであり、通常、発生後に処理する必要はありません。このエラーが最初に表示されないようにコードを修正することができます。(Eq。NLP、クラスキャスト、ゼロ除算など)

于 2011-07-06T07:17:00.930 に答える
0

一般に、上位レベルのコードでRuntimeExceptionを処理できず、例外について何もできない場合は、RuntimeExceptionをスローすることをお勧めします。

あなたの場合、APIは、結果が成功した場合は何らかの結果を返し、結果が見つからない場合はnullを返し、操作の実行中に問題が発生した場合は例外をスローします。

ユーザーにエラーメッセージを表示しているだけで、問題について何もしていない場合は、例外をオフにすることができます。

一方、問題が発生した場合に別の手順を実行する予定の場合は、チェックされた例外をスローする必要があります。

たとえば、私は次のようなコードを持っています-

支払いを差し引いてから、発送の詳細を送信します。(出荷の詳細の送信が成功しなかった場合)場合は、支払いを返金します。それ以外の場合は、成功メールを送信します。

上記のロジックでは、配送の詳細を送信するロジックは、チェックされた例外をスローする必要があります。問題がある場合は、金額を顧客に返金する必要があります。この場合、チェックされていない例外をスローすると、ユーザーにエラーメッセージが表示される可能性があります(runtimeexceptionをキャッチしない限り、これは悪いことです)。

ありがとう、サスウィック

于 2011-07-06T06:13:24.917 に答える
0

チェックされていない例外は、コードの乱雑さを回避するのに役立ちます。たとえば、このコードを考えてみましょう

    try
    {
        File file = new File("file");
        FileReader fileReader = new FileReader(file);
        BufferedReader bufferedReader = new BufferedReader(fileReader);
        String temp = "";
        StringBuilder result = new StringBuilder("");
        while ((temp = bufferedReader.readLine()) != null)
        {
            result.append(temp);
        }
        System.out.println(result);
    }
    catch (FileNotFoundException e)
    {
        e.printStackTrace();
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }

2つの例外タイプを処理しますが、これらの例外を処理することで発生する可能性のある実行可能なイベントがない場合、そもそもなぜそれらをキャッチするのでしょうか。答えは、チェックされた例外をスローしないことです。最終的には、呼び出し階層の上位のクラスがそれを処理する必要があるためです(または、JVMにスローされます)。開発者がこれらの処理に本当に興味がある場合は、問題に対処する特定のRuntimeExceptionクラスをキャッチします。

複数の例外を保持するキャッチブロックは、この混乱を減らすのに何らかの方法で役立ちます

于 2011-07-06T06:35:39.393 に答える