web-dev-qa-db-ja.com

大規模なシステムで使用されるメソッドの試行/キャッチまたはメソッドシグネチャへの例外の追加?

_public Path createPath(String name){
    return Files.createFile( Paths.get( name ) );
}
_

createPath(String name)では、Java.nio.file.FilesがJava.io.IOExceptionをスローします。ただし、これはより大きなライブラリで使用されます。そして、私は巨大です。メソッドシグネチャに例外を追加するだけの場合、それは、より大きなライブラリがそれを使用するすべてのメソッドにも例外を追加する必要があることを意味します。トライキャッチでラップすると、おそらく1日を節約できますが、コードが見苦しくなります。 (私の目で):

_public Path createPath(String name){
    Path file = null;
    try {
        file = Files.createFile( Paths.get( name ) );
    } catch ( IOException e ) {
        e.printStackTrace();
    }
    return file;
}
_

これのための最良のアプローチは何でしょうか?

2
Rigo Sarmiento

次の2つのオプションが考えられます。

  1. createPathは例外を飲み込み、アプリケーションはNullPointerException。のどこかでクラッシュします。
    • 実際、正しい方法は、RuntimeExceptionまたはErrorまたはそれらのいずれかのサブクラスをスローすることです。
  2. mainまでのコールチェーン全体にthrows IOExceptionを追加し、IOExceptionでアプリケーションをクラッシュさせます。

ただし、チェック済み例外(または一般的な例外)の背後にある実際の意図は異なります。

  1. throws IOExceptionを、ファイルを作成できない場合の対処方法を知っている呼び出し元までのコールチェーンに追加します。呼び出し元はIOExceptionをキャッチして処理します。

次の例を検討してください。

import Java.io.IOException;
import Java.nio.file.Files;
import Java.nio.file.Path;
import Java.nio.file.Paths;
import Java.util.Scanner;

public class App {
    private Scanner reader;

    public App() {
        reader = new Scanner(System.in);
    }

    public String getPathFromUser() {
        System.err.print("Enter a path: ");
        return reader.nextLine();
    }

    public Path createPath(String name) throws IOException {
        return Files.createFile(Paths.get(name));
    }

    public Path promptForPath() {
        while (true) {
            String path = getPathFromUser();
            try {
                return createPath(path);
            } catch (IOException e) {
                System.err.printf("Unable to create %s (%s)\n", path, e);
                continue;
            }
        }
    }

    public static void main(String[] args) {
        App app = new App();
        Path path = app.promptForPath();
        // do stuff with path
    }
}

ここでmainpromptForPathを呼び出し、次にcreatePathを呼び出します。

  • promptForPathはそのIOExceptionを処理する方法を知っているため(ユーザーに別のパスを要求します)、throw IOExceptionでマークする必要はありません。
  • createPathはその処理方法を認識していないため、throw IOExceptionである必要があります。
  • mainもそれを処理する方法を知りません-しかし、promptForPathがすでにそれを処理したので、それをする必要はありません。

throws句を使用してコールスタック内のメソッドを汚染するか、createPath...tryを使用してcatchを醜くする方が良いですか?

これらのものは、間違った場所に置かれたときにのみ醜くて扱いにくいです。適切な場所に配置すると、醜い汚染ではなく、ロジックの一部として実際に適合できます。

質問のtrycatch...createPathは、醜いコード臭です-それが存在することは明らかですhide a問題。一方、trycatch...promptForPathは、問題を処理しているため、見苦しくありません。

同様に、トップレベルのメソッドのthrows IOExceptionは醜いです-なぜあなたのmainIOExceptionをスローするのでしょうか?しかし、パスを取得する下位のメソッドでは意味があります( "このメソッドはパスを処理し、存在しないパスに遭遇する可能性があります")ので、醜くない-それらは署名の役に立つ部分です。

考慮すべきもう1つのこと:例外を完全にバブリングする場合は、1つの例外を別の例外に置き換えるだけです。それが仕事の価値があるかどうかわからない。しかし、それを処理できるポイントまでバブルしている場合は、アプリケーションの品質が向上します。たとえば、クラッシュする代わりに、入力したパスが無効であることをユーザーに通知し、入力を促す新しいもの。これはユーザー側からの望ましい動作であり、リファクタリングを価値のあるものにします。

3
Idan Arye

何よりもまず、正しい答えはこの1つの簡単な質問に対処する必要があります。

この方法ではどのような保証が必要ですか?

要するに、要件は何ですか?ファイルを作成するメソッドをリクエストしていて、そのファイルを作成しない場合は、その目的を満たしていません。そのファイルを作成できない理由には、アクセス許可から、ファイルを作成することになっているパスが存在しない、または存在できないなど、さまざまな理由が考えられます。

この時点で、3つのオプションがあります。

  • 問題を無視します。問題とならないこともありますが、単に無視して問題がなくなることを期待してください。それはしません。
  • 成功または失敗、潜在的にはどのような失敗を示すステータスを返します。
  • 親ライブラリがキャッチする必要がある例外をスローします。もちろん、次の質問は、チェック例外を宣言するか、実行時例外を使用するかです。

この場合、ファイルを作成できない場合、所有しているライブラリがその状態から回復し、別のファイルを作成できる可能性があります。または、回復できない可能性があり、その大きなライブラリを使用しているエンドコードに何かが本当に間違っていることを通知する前に、いくつかのクリーンアップを行う必要があります。

問題を黙って無視することで、ライブラリを作成していますassume操作が正常に完了しなかったときに正常に完了しました。無効な仮定は、微妙なエラーではなく、微妙なエラーの原因となります。操作の成功または失敗を明確に伝えて、その関数を使用するコードが既知の状態で操作できるようにする必要があります。

例外がチェックされるかどうかの問題は、どのような苦痛を我慢したいのかの1つです。

  • チェック例外は、処理するか、明示的に渡す必要がある例外です。これには、コード内の例外をどうするかをすぐに決定するという苦痛があります。
  • ランタイム例外は宣言する必要のない例外ですが、必要に応じてキャッチできます。 (Java以外の言語では、これは通常サポートされる唯一の種類の例外です)。これに耐えるのは、例外が発生して処理されない場合、大きなライブラリを使用してアプリケーションをクラッシュさせることです。

例外がスローされると思われる可能性と、例外が直接処理されない場合のシステムへの影響の大きさに基づいて、決定を行う必要があります。

4
Berin Loritsch