例外:なぜ早く投げるのですか?なぜ遅れるのか?


156

単独での例外処理に関する多くの有名なベストプラクティスがあります。「やるべきこと」は十分に知っていますが、大規模な環境でのベストプラクティスやパターンに関しては複雑になります。「早く投げて、遅れて捕まえる」-私は何度も聞いたことがありますが、それでも私を混乱させます。

低レベル層でNULLポインター例外がスローされる場合、なぜ早くスローしてキャッチする必要があるのですか?なぜ上位のレイヤーでキャッチする必要があるのですか?ビジネスレイヤーなど、高レベルで低レベルの例外をキャッチするのは意味がありません。各層の懸念に違反しているようです。

次の状況を想像してください。

数値を計算するサービスがあります。この数値を計算するために、サービスはリポジトリにアクセスして生データを取得し、他のサービスが計算を準備します。データ取得レイヤーで何か問題が発生した場合、なぜDataRetrievalExceptionをより高いレベルにスローする必要があるのですか?対照的に、CalculationServiceExceptionなどの意味のある例外に例外をラップすることをお勧めします。

なぜ早く投げ、なぜ遅くキャッチするのですか?


104
「遅いキャッチ」の背後にある考え方は、できるだけ遅くキャッチするのではなく、できるだけ早くキャッ​​チすることです。たとえば、ファイルパーサーがある場合、見つからないファイルを処理しても意味がありません。あなたはそれをどうするつもりですか、あなたの回復経路は何ですか?ありませんので、キャッチしないでください。あなたのレクサーに行き、そこで何をしますか、あなたのプログラムが継続できるようにどのようにこれから回復しますか?それはできません、例外を通過させます。スキャナーはこれをどのように処理できますか?それを渡すことはできません。呼び出しコードはこれをどのように処理できますか?別のファイルパスを試すか、ユーザーに警告して、キャッチします。
Phoshi

16
NullPointerException(私はそれがNPEの意味だと思います)をキャッチすべきケースはほとんどありません。可能であれば、そもそも避けるべきです。NullPointerExceptionsが発生している場合は、修正が必要な壊れたコードがあります。これもおそらく簡単な修正です。
フィル

6
この質問を重複して閉じることを提案する前に、他の質問への回答がこの質問にあまりよく答えていないことを確認してください。
Doc Brown 14

1
(Citebot)today.java.net/article/2003/11/20/…これが引用の起源でない場合は、最も可能性が高いと思われるソースへの参照を提供してください。
rwong

1
この質問に到達し、Android開発を行っている人へのリマインダーです。Androidでは、例外は最初にキャッチされたのと同じ関数でローカルでキャッチされ処理される必要があります。これは、例外がメッセージハンドラー全体に伝播しないためです。発生した場合、アプリケーションは強制終了されます。そのため、Android開発を行う際にこのアドバイスを引用しないでください。
rwong 14年

回答:


118

私の経験では、エラーが発生した時点で例外をスローすることが最善です。これは、例外がトリガーされた理由について最もよく知っているポイントだからです。

例外がレイヤーを巻き戻すので、キャッチと再スローは例外にコンテキストを追加する良い方法です。これは、異なるタイプの例外をスローすることを意味する場合がありますが、これを行う場合は元の例外を含めます。

最終的に、例外はコードフローに関する決定を下すことができるレイヤーに到達します(例えば、ユーザーにアクションを促す)。これが最終的に例外を処理し、通常の実行を継続する必要があるポイントです。

コードベースの実践と経験により、エラーにコンテキストを追加するタイミングと、実際にエラーを処理するのが最も賢明なタイミングを最終的に判断することが非常に簡単になります。

キャッチ→再スロー

これは、開発者が問題を理解するためにすべてのレイヤーを介して作業しなければならない情報を追加できる場合に役立ちます。

キャッチ→ハンドル

これは、ソフトウェアの適切であるが異なる実行フローについて最終決定を下せる場合に行います。

キャッチ→エラーリターン

これが適切な状況もありますが、Catch→Rethrowの実装にリファクタリングするには、例外をキャッチして呼び出し元にエラー値を返すことを検討する必要があります。


はい、エラーが発生した時点で例外をスローする必要があることはすでにわかっています。しかし、なぜNPEをキャッチし、代わりにStacktraceに登らせてはいけないのでしょうか?私は常にNPEをキャッチし、意味のある例外にラップしていました。また、サービスまたはUI層までDAO-Exceptionをスローする必要がある理由もわかりません。私は常にサービス層でそれをキャッチし、追加の詳細情報、サービスの呼び出しが失敗した理由を含むサービス例外にラップします。
shylynx 14

8
@shylynx例外をキャッチしてから、より意味のある例外を再スローするのは良いことです。してはいけないことは、例外を早すぎる段階でキャッチしてから、再スローしないことです。ことわざが警告している間違いは、例外を早すぎる段階でキャッチし、コード内の間違ったレイヤーで処理しようとすることです。
サイモンB

例外を受け取った時点でコンテキストを明確にすると、チームの開発者が楽になります。NPEは、問題を理解するために、さらなる調査が必要である
マイケル・ショー

4
@shylynx質問をするかもしれません、「なぜコードを投げることができるポイントがあるのNullPointerExceptionでしょうか?呼び出し元が不良がどこに渡されたかを正確に知るためにnull、例外をチェックしてスローするのはなぜですか?」私はそれがsayingの「早く投げる」部分が示唆するものだと信じています。IllegalArgumentExceptionnull
jpmc26 14年

2
@jpmcレイヤーと例外に関する懸念を強調するために、NPEを例としてのみ取り上げました。IllegalArgumentExceptionに置き換えることもできます。
shylynx 14年

56

原因を見つけやすくするため、できるだけ早く例外をスローする必要があります。たとえば、特定の引数で失敗する可能性のあるメソッドを考えます。引数を検証し、メソッドの最初で失敗した場合、呼び出し元のコードにエラーがあることがすぐにわかります。失敗する前に引数が必要になるまで待つ場合、実行を追跡し、バグが呼び出しコードにあるか(引数が間違っている)、メソッドにバグがあるかどうかを把握する必要があります。例外を早くスローするほど、根本的な原因に近くなり、どこで問題が発生したかを簡単に把握できます。

例外がより高いレベルで処理される理由は、より低いレベルでは、エラーを処理するための適切なアクションがわからないためです。実際、呼び出しコードが何であるかに応じて、同じエラーを処理する複数の適切な方法があります。たとえば、ファイルを開くとします。構成ファイルを開こうとしてもそこにない場合は、例外を無視してデフォルト構成を続行することが適切な応答になる可能性があります。プログラムの実行に不可欠なプライベートファイルを開いており、それが何らかの形で欠落している場合、おそらく唯一のオプションはプログラムを閉じることです。

例外を適切なタイプにラップすることは、純粋に直交する関心事です。


1
異なるレベルが重要である理由を明確に説明するために+1。ファイルシステムエラーの優れた例。
フアンカルロスコト

24

他の人はなぜ早く投げるのかをかなりよく要約しました。代わりに後半部分をキャッチする理由に集中してみましょう。これについては、私の好みについて満足のいく説明がありませんでした。

なぜ例外なのか?

そもそも例外が存在する理由についてはかなり混乱しているようです。ここで大きな秘密を共有しましょう:例外の理由と例外処理は... ABSTRACTIONです。

このようなコードを見ましたか:

static int divide(int dividend, int divisor) throws DivideByZeroException {
    if (divisor == 0)
        throw new DivideByZeroException(); // that's a checked exception indeed

    return dividend / divisor;
}

static void doDivide() {
    int a = readInt();
    int b = readInt(); 
    try {
        int res = divide(a, b);
        System.out.println(res);
    } catch (DivideByZeroException e) {
        // checked exception... I'm forced to handle it!
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

これは、例外の使用方法ではありません。上記のようなコードは実際に存在しますが、より異常なものであり、実際には例外です(しゃれ)。たとえば、純粋な数学であっても、除算の定義は条件付きです。入力ドメインを制限するためにゼロの例外的なケースを処理する必要があるのは、常に「呼び出し元コード」です。それは醜いです。発信者にとって常に苦痛です。それでも、このような状況では、check-then-doパターンが自然な方法です。

static int divide(int dividend, int divisor) {
    // throws unchecked ArithmeticException for 0 divisor
    return dividend / divisor;
}

static void doDivide() {
    int a = readInt();
    int b = readInt();
    if (b != 0) {
        int res = divide(a, b);
        System.out.println(res);
    } else {
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

または、次のようにOOPスタイルで完全なコマンドを実行できます。

static class Division {
    final int dividend;
    final int divisor;

    private Division(int dividend, int divisor) {
        this.dividend = dividend;
        this.divisor = divisor;
    }

    public boolean check() {
        return divisor != 0;
    }

    public int eval() {
        return dividend / divisor;
    }

    public static Division with(int dividend, int divisor) {
        return new Division(dividend, divisor);
    }
}

static void doDivide() {
    int a = readInt();
    int b = readInt(); 
    Division d = Division.with(a, b);
    if (d.check()) {
        int res = d.eval();
        System.out.println(res);
    } else {
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

ご覧のとおり、呼び出し元のコードには事前チェックの負担がありますが、その後の例外処理は行いません。場合はArithmeticException、これまでの呼び出しから来ているdivideか、evalそして、それはあなたがあなたが忘れてしまったので、例外処理を行うと、あなたのコードを修正しなければならない人check()。同様の理由で、aをキャッチすることNullPointerExceptionはほとんど常に間違ったことです。

現在、メソッド/関数のシグネチャの例外的なケースを見たい、つまり出力ドメインを明示的に拡張したいという人々がいます。彼らはチェック例外を好むものです。もちろん、出力ドメインを変更すると、直接の呼び出し元コードを強制的に適合させる必要があります。これは、チェック例外を使用して実現できます。しかし、そのための例外は必要ありません!そのため、Nullable<T> ジェネリッククラスケースクラス代数データ型、およびユニオン型があります一部のオブジェクト指向の人々は、次のような単純なエラーの場合に戻る ことを好むかもしれませんnull

static Integer divide(int dividend, int divisor) {
    if (divisor == 0) return null;
    return dividend / divisor;
}

static void doDivide() {
    int a = readInt();
    int b = readInt(); 
    Integer res = divide(a, b);
    if (res != null) {
        System.out.println(res);
    } else {
        System.out.println("Nah, can't divide by zero. Try again.");
    }
}

技術的に、上記のような目的で例外使用できますが、ここにポイントがありますそのような使用法には例外は存在しません。例外は抽象概念です。例外は、間接指定に関するものです。例外により、直接的なクライアント契約を破ることなく「結果」ドメインを拡張し、エラー処理を「他の場所」に延期することができます。コードが、同じコードの直接の呼び出し元で処理される例外をスローし、その間に抽象レイヤーが含まれていない場合、それは間違っています

遅延をキャッチする方法は?

だからここにいる。上記のシナリオで例外を使用することは、例外をどのように使用するかではないことを示すために、私の方法を議論しました。ただし、例外処理によって提供される抽象化と間接化が不可欠である、真のユースケースが存在します。そのような使用法を理解することは、キャッチレイトの推奨事項の理解にも役立ちます。

その使用例は次のとおりです。リソース抽象化に対するプログラミング ...

ええ、ビジネスロジックは、具体的な実装ではなく、抽象化に対してプログラムする必要があります。トップレベルのIOC「配線」コードは、リソース抽象化の具体的な実装をインスタンス化し、それらをビジネスロジックに渡します。ここに新しいものはありません。しかし、これらのリソース抽象化の具体的な実装は、潜在的に独自の実装固有の例外をスローする可能性がありますか?

実装固有の例外を処理できるのは誰ですか?その場合、ビジネスロジックでリソース固有の例外を処理することは可能ですか?いいえ、そうではありません。ビジネスロジックは抽象化に対してプログラムされており、これらの実装固有の例外の詳細に関する知識は除外されます。

「Aha!」と言うかもしれませんが、「だから例外をサブクラス化して例外階層を作成できるのです」(Mr. Springをチェックしてください!)。誤解です。まず、OOPに関するすべての合理的な本は、具体的な継承は悪いと述べていますが、JVMのこのコアコンポーネントである例外処理は、具体的な継承と密接に結びついています。皮肉なことに、Joshua Blochは、有効なJavaの本を書く前に、動作するJVMでの経験を得ることができませんでしたか?それは、次世代のための「学んだ教訓」の本です。第二に、そしてより重要なこととして、高レベルの例外をキャッチした場合、どのようにそれを処理しますか?PatientNeedsImmediateAttentionException:私たちは彼女にピルを与えるか、彼女の足を切断する必要があります!?考えられるすべてのサブクラスに対するswitchステートメントはどうですか?ポリモーフィズムがあり、抽象化があります。あなたはポイントを得ました。

それでは、リソース固有の例外を誰が処理できますか?結石を知っている人でなければなりません!リソースをインスタンス化した人!もちろん「配線」コード!これをチェックしてください:

抽象化に対してコーディングされたビジネスロジック...コンクリートリソースエラー処理なし!

static interface InputResource {
    String fetchData();
}

static interface OutputResource {
    void writeData(String data);
}

static void doMyBusiness(InputResource in, OutputResource out, int times) {
    for (int i = 0; i < times; i++) {
        System.out.println("fetching data");
        String data = in.fetchData();
        System.out.println("outputting data");
        out.writeData(data);
    }
}

一方、どこか他の具体的な実装...

static class ConstantInputResource implements InputResource {
    @Override
    public String fetchData() {
        return "Hello World!";
    }
}

static class FailingInputResourceException extends RuntimeException {
    public FailingInputResourceException(String message) {
        super(message);
    }
}

static class FailingInputResource implements InputResource {
    @Override
    public String fetchData() {
        throw new FailingInputResourceException("I am a complete failure!");
    }
}

static class StandardOutputResource implements OutputResource {
    @Override
    public void writeData(String data) {
        System.out.println("DATA: " + data);
    }
}

そして最後に、配線コード...具体的なリソース例外を処理するのは誰ですか?それらについて知っている人!

static void start() {
    InputResource in1 = new FailingInputResource();
    InputResource in2 = new ConstantInputResource();
    OutputResource out = new StandardOutputResource();

    try {
        ReusableBusinessLogicClass.doMyBusiness(in1, out, 3);
    }
    catch (FailingInputResourceException e)
    {
        System.out.println(e.getMessage());
        System.out.println("retrying...");
        ReusableBusinessLogicClass.doMyBusiness(in2, out, 3);
    }
}

今私と一緒に耐えます。上記のコード単純です。IOCコンテナー管理リソースの複数のスコープを持つエンタープライズアプリケーション/ Webコンテナーがあり、セッションまたはリクエストスコープリソースの自動再試行と再初期化などが必要であると言うかもしれません。リソースを作成するため、正確な実装を認識しません。高レベルのスコープのみが、それらの低レベルのリソースがスローできる例外を本当に知っているでしょう。ちょっと待って!

残念ながら、例外はコールスタックを介した間接化のみを許可し、異なるカーディナリティを持つ異なるスコープは通常、複数の異なるスレッドで実行されます。例外を除き、それを介して通信する方法はありません。ここにはもっと強力なものが必要です。回答:非同期メッセージパッシング。下位レベルのスコープのルートですべての例外をキャッチします。何も無視しないでください。何もすり抜けないでください。これにより、現在のスコープの呼び出しスタックで作成されたすべてのリソースが閉じられ、破棄されます。次に、例外が既知のレベルに達するまで、例外処理ルーチンでメッセージキュー/チャネルを使用して、エラーメッセージを上位のスコープに伝達します。それはそれを処理する方法を知っている人です。

SUMMA SUMMARUM

だから、私の解釈によれば、キャッチレイトとは、あなたがこれ以上アブストラクトを壊さない最も便利な場所で例外をキャッチすることを意味します。早くキャッ​​チしないでください!リソース抽象化のインスタンスをスローする具体的な例外を作成するレイヤー、抽象化の結石を知っているレイヤーで例外をキャッチします。「配線」層。

HTH。ハッピーコーディング!


インターフェイスを提供するコードは、インターフェイスを使用するコードよりも何が間違っているかを知っていることは正しいですが、メソッドが同じインターフェイスタイプの2つのリソースを使用し、障害を異なる方法で処理する必要があると仮定しますか?または、それらのリソースの1つが、その作成者に知られていない実装の詳細として、同じタイプの他のネストされたリソースを使用する場合 ビジネス層のスローを持つWrappedFirstResourceExceptionWrappedSecondResourceException...と、問題の根本的な原因を確認するために、その例外内部を見るために「配線」層を必要とする
supercat

...厄介かもしれませんが、FailingInputResource例外はを使用した操作の結果であると想定するよりも良いと思われin1ます。実際、多くの場合、適切なアプローチは、配線層に例外処理オブジェクトを渡し、ビジネス層にcatchそのオブジェクトのhandleExceptionメソッドを呼び出すa を含めることだと思います。このメソッドは、アプリケーションが必要とする内容に応じて、再スロー、デフォルトデータの提供、または「中止/再試行/失敗」プロンプトを表示し、オペレータに対処方法などを決定させることができます。
supercat

@supercatあなたが言っていることを理解しています。具体的なリソース実装は、スローできる例外を知る責任があります。すべてを指定する必要はありません(いわゆる未定義の動作と呼ばれます)が、あいまいさが存在しないことを確認する必要があります。また、未チェックの実行時例外も文書化されます。ドキュメントと矛盾する場合、それはバグです。呼び出し元のコードが例外について賢明なことを何でもすると予想される場合、最低限は、リソースがUnrecoverableInternalExceptionHTTP 500エラーコードのようにそれらをいくつかにラップすることです。
ダニエル・ディニーズ14

@supercat構成可能なエラーハンドラに関する提案について:まさに!私の最後の例では、エラー処理ロジックはハードコードされており、静的doMyBusinessメソッドを呼び出しています。これは簡潔にするためであり、より動的にすることは完全に可能です。このようなHandlerクラスは、いくつかの入出力リソースでインスタンス化handleされ、を実装するクラスを受け取るメソッドを持ちReusableBusinessLogicInterfaceます。次に、それらの上のどこかの配線層で異なるハンドラー、リソース、およびビジネスロジックの実装を使用するように結合/構成できます。
ダニエルディニーズ14

10

この質問に適切に答えるために、一歩戻って、さらに基本的な質問をしてみましょう。

そもそも例外があるのはなぜですか?

メソッドの呼び出し元に、要求されたことを実行できないことを知らせるために、例外をスローします。例外のタイプは、私たちがやりたいことをできなかった理由を説明しています。

いくつかのコードを見てみましょう。

double MethodA()
{
    return PropertyA - PropertyB.NestedProperty;
}

nullの場合、このコードは明らかにnull参照例外をスローできPropertyBます。この場合、この状況を「修正」するためにできることは2つあります。我々は出来た:

  • PropertyBがない場合は自動的に作成します。または
  • 呼び出し元のメソッドまで例外をバブルさせます。

ここでPropertyBを作成することは非常に危険です。このメソッドがPropertyBを作成しなければならない理由は何ですか?確かに、これは単一の責任原則に違反するでしょう。おそらく、ここにPropertyBが存在しない場合、何かが間違っていることを示しています。メソッドが部分的に構築されたオブジェクトで呼び出されているか、PropertyBが誤ってnullに設定されました。ここでPropertyBを作成することで、データ破損の原因となるバグなど、後で噛むことができるはるかに大きなバグを隠すことができます。

代わりに、null参照をバブルアップさせた場合、このメソッドを呼び出した開発者に、できる限り早く問題が発生したことを知らせます。このメソッドを呼び出すための重要な前提条件が欠落しています。

したがって、実際には、懸念をより良く分離するため、早めに投げています。障害が発生するとすぐに、アップストリームの開発者に通知します。

なぜ「遅れる」のかは別の話です。私たちは本当に遅れて捕まえたくありません。問題を適切に処理する方法を知っている限り早く捕まえたいのです。いくつかの場合、これは後で15層の抽象化になり、いくつかの場合、作成の時点になります。

要点は、例外を適切に処理するために必要なすべての情報が揃った時点で例外を処理できる抽象化層で例外をキャッチすることです。


上流の開発者を間違った意味で使用していると思います。また、あなたはそれが単一責任の原則に反しているが、需要の初期化と価値のキャッシュ上の多くの現実に(もちろんの代わりに適切な並行性制御で)そのように実装されていた
ダニエルDinnyes

あなたの例では、減算操作の前にnullをチェックするのはどうif(PropertyB == null) return 0;
ですか?user1451111

1
また、あなたの最後の段落に手の込んだことができ、特にあなたは「で何を意味抽象化レイヤ」。
user1451111

IOの作業を行う場合、IO例外をキャッチする抽象化の層は、作業を行う場所になります。その時点で、再試行するか、ユーザーにメッセージボックスを表示するか、デフォルトのセットを使用してオブジェクトを作成するかを決定する必要があるすべての情報があります。
スティーブン

「与えられた例では、このif(PropertyB == null)が0を返すように、減算操作の前にnullをチェックするのはどうですか。」うん それは、呼び出し元のメソッドに、減算する有効なものがあることを伝えることになります。もちろんこれは文脈に依存しますが、ほとんどの場合、ここでエラーチェックを行うのは悪い習慣です。
スティーブン

6

オブジェクトを無効な状態にしないために、投げる価値のあるものが見つかったらすぐに投げます。NULLポインタが渡された場合は早期にそれをチェックし、NPEをスローすることを意味する前に、それがローレベルにトリクルダウンする機会を持っています。

エラーを修正するために何をすべきかを知ったらすぐにキャッチします(通常はif-elseを使用することができます) 。


1
あなたは書いた:すぐに投げる、...すぐにキャッチ...!どうして?「早めに投げて、遅れて捕まえる」とは対照的に、これはまったく逆のアプローチです。
shylynx 14

1
@shylynx「早く投げて、遅くキャッチ」がどこから来たのかはわかりませんが、その価値には疑問があります。正確に言うと、キャッチ "late"はどういう意味ですか?例外をキャッチすることが理にかなっている場合(あるとしても)は、問題によって異なります。明確なことは、できるだけ早く問題を検出(およびスロー)することです。
ドーバル14

2
「キャッチレイト」は、エラーを修正するために何をすべきかを知るにキャッチする慣行を対比することを意図していると仮定します。

@Hurkyl:「catch late」の問題は、例外がそれについて何も知らないレイヤーにバブルアップすると、状況について何かをする立場にあるかもしれないコードが、物事が本当に期待した。簡単な例として、ユーザードキュメントファイルのパーサーがディスクからCODECを読み込む必要があり、その読み取り中にディスクエラーが発生した場合、ユーザーの読み取り中にディスクエラーがあると考えた場合、パーサーを呼び出すコードが不適切に動作する可能性があるとします資料。
supercat

4

有効なビジネスルールは、「下位レベルのソフトウェアが値の計算に失敗した場合、...」です。

これは上位レベルでのみ表現できます。そうでない場合、下位レベルのソフトウェアは自身の正確さに基づいて動作を変更しようとします。


2

まず、例外は例外的な状況です。この例では、生データがロードできなかったために存在しない場合、数値は計算できません。

私の経験から、スタックを上るときに例外を抽象化することをお勧めします。通常、これを実行するポイントは、例外が2つのレイヤーの境界を越えるときです。

データレイヤーで生データを収集する際にエラーが発生した場合は、例外をスローして、データを要求した人に通知します。ここでこの問題を回避しようとしないでください。処理コードの複雑さは非常に高くなる可能性があります。また、データレイヤーはデータのリクエストのみを担当し、これを実行中に発生したエラーの処理は担当しません。これは、「早く投げる」という意味です。

あなたの例では、キャッチ層はサービス層です。サービス自体は新しい層であり、データアクセス層の上にあります。そこで、例外をキャッチしたいのです。サービスにフェールオーバーインフラストラクチャがあり、別のリポジトリからデータを要求しようとしている可能性があります。これも失敗する場合は、サービスの呼び出し元が理解できるものの中に例外をラップします(Webサービスの場合、これはSOAPフォールトである可能性があります)。元の例外を内部例外として設定し、後の層が何が問題なのかを正確に記録できるようにします。

サービス障害は、サービスを呼び出すレイヤー(たとえば、UI)によってキャッチされる場合があります。そして、これが"キャッチキャッチ"の意味です。下位層で例外を処理できない場合は、再スローします。最上位のレイヤーが例外を処理できない場合、それを処理します!これには、ロギングまたは表示が含まれる場合があります。

例外を再スローする必要がある理由は(上記で説明したように、より一般的な例外でラップすることで)、たとえば、ポインターが無効なメモリを指しているためにエラーが発生したことをユーザーが理解できない可能性が高いためです。そして彼は気にしません。彼は、数字がサービスによって計算できなかったことにのみ気をつけています。これは彼に表示されるべき情報です。

さらに進むと(理想的な世界では)UIから完全に除外try/ catchコード化できます。代わりに、下位層によってスローされる可能性のある例外を理解し、ログに書き込み、エラーの意味のある(およびローカライズされた)情報を含むエラーオブジェクトにラップするグローバル例外ハンドラーを使用します。これらのオブジェクトは、任意の形式(メッセージボックス、通知、メッセージトーストなど)でユーザーに簡単に提示できます。


1

壊れたコントラクトが必要以上にコードを流れてほしくないので、一般に早期に例外を投げることは良い習慣です。たとえば、特定の関数パラメーターが正の整数であると予想される場合、その変数がコードスタックのどこかで使用されるまで待機するのではなく、関数呼び出しの時点でその制約を適用する必要があります。

私は自分のルールがあり、プロジェクトごとに変化するため、遅くキャッチすることはできません。しかし、私がやろうとしていることの1つは、例外を2つのグループに分けることです。1つは内部使用専用で、もう1つは外部使用専用です。内部例外は、独自のコードによってキャッチおよび処理され、外部例外は、私を呼び出しているコードによって処理されることを意図しています。これは基本的には後で物事をキャッチする形式ですが、内部コードで必要なときにルールから逸脱する柔軟性を提供するため、まったくそうではありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.