web-dev-qa-db-ja.com

Javaでエラーコード/文字列を定義する最良の方法は?

私はJavaでWebサービスを書いています。エラーコードとそれに関連するエラー文字列を定義する最良の方法を見つけようとしていますです。数値エラーコードとエラー文字列をグループ化する必要があります。エラーコードとエラー文字列の両方が、Webサービスにアクセスするクライアントに送信されます。たとえば、SQLExceptionが発生した場合、次のことを実行できます。

// Example: errorCode = 1, 
//          errorString = "There was a problem accessing the database."
throw new SomeWebServiceException(errorCode, errorString);

クライアントプログラムに次のメッセージが表示される場合があります。

「エラー#1が発生しました。データベースへのアクセスに問題がありました。」

最初に考えたのは、エラーコードのEnumを使用し、toStringメソッドをオーバーライドしてエラー文字列を返すことでした。ここに私が思いついたものがあります:

public enum Errors {
  DATABASE {
    @Override
    public String toString() {
      return "A database error has occured.";
    }
  },

  DUPLICATE_USER {
    @Override
    public String toString() {
      return "This user already exists.";
    }
  },

  // more errors follow
}

私の質問は:これを行うより良い方法はありますか?外部ファイルから読み取るよりも、コードで解決する方がいいと思います。私はこのプロジェクトにJavadocを使用していますが、エラーコードをインラインでドキュメント化し、ドキュメント内でエラーコードを自動的に更新できると便利です。

113
William Brendel

確かに列挙型ソリューションのより良い実装は確かにあります(一般的には非常にいいです):

public enum Error {
  DATABASE(0, "A database error has occured."),
  DUPLICATE_USER(1, "This user already exists.");

  private final int code;
  private final String description;

  private Error(int code, String description) {
    this.code = code;
    this.description = description;
  }

  public String getDescription() {
     return description;
  }

  public int getCode() {
     return code;
  }

  @Override
  public String toString() {
    return code + ": " + description;
  }
}

代わりに、単に説明を返すためにtoString()をオーバーライドすることもできます-確かではありません。とにかく、主なポイントは、エラーコードごとに個別にオーバーライドする必要がないことです。また、序数値を使用する代わりにコードを明示的に指定したことに注意してください。これにより、後で順序を変更したり、エラーを追加/削除したりしやすくなります。

これはまったく国際化されていないことを忘れないでください-ただし、Webサービスクライアントがロケールの説明を送信しない限り、とにかく自分で簡単に国際化することはできません。少なくとも、クライアント側でi18nに使用するエラーコードがあります...

150
Jon Skeet

私に関する限り、プロパティファイルのエラーメッセージを外部化することを好みます。これは、アプリケーションを国際化する場合に非常に役立ちます(言語ごとに1つのプロパティファイル)。また、エラーメッセージを変更する方が簡単であり、Javaソースを再コンパイルする必要はありません。

私のプロジェクトでは、通常、エラーコード(文字列または整数、あまり関係ありません)を含むインターフェイスがあり、このエラーのプロパティファイルにキーが含まれています。

public interface ErrorCodes {
    String DATABASE_ERROR = "DATABASE_ERROR";
    String DUPLICATE_USER = "DUPLICATE_USER";
    ...
}

プロパティファイル内:

DATABASE_ERROR=An error occurred in the database.
DUPLICATE_USER=The user already exists.
...

ソリューションのもう1つの問題は保守性です。エラーは2つだけで、すでに12行のコードがあります。管理するエラーが何百もある場合、列挙ファイルを想像してください!

33
Romain Linsolas

ToString()をオーバーロードするのは少し厄介に思えます-それはtoString()の通常の使用法の一部のようです。

どうですか:

public enum Errors {
  DATABASE(1, "A database error has occured."),
  DUPLICATE_USER(5007, "This user already exists.");
  //... add more cases here ...

  private final int id;
  private final String message;

  Errors(int id, String message) {
     this.id = id;
     this.message = message;
  }

  public int getId() { return id; }
  public String getMessage() { return message; }
}

私にはずっときれいに見えます...

20
Cowan

私の最後の仕事で、enumバージョンをもう少し深くしました。

public enum Messages {
    @Error
    @Text("You can''t put a {0} in a {1}")
    XYZ00001_CONTAINMENT_NOT_ALLOWED,
    ...
}

@ Error、@ Info、@ Warningはクラスファイルに保持され、実行時に利用可能です。 (メッセージ配信の説明に役立つ他の注釈もいくつかありました)

@Textはコンパイル時の注釈です。

このために、次の処理を行う注釈プロセッサを作成しました。

  • メッセージ番号が重複していないことを確認します(最初のアンダースコアの前の部分)
  • メッセージテキストの構文チェック
  • Enum値をキーとするテキストを含むmessages.propertiesファイルを生成します。

エラーをログに記録し、それらを例外としてラップする(必要な場合)などのユーティリティルーチンをいくつか作成しました。

私は彼らに私にそれをオープンソースにさせようとしています...-スコット

17

Java.util.ResourceBundleをご覧になることをお勧めします。 I18Nを気にする必要がありますが、気にしなくても価値があります。メッセージを外部化することは非常に良い考えです。私は、見たいと思う言語を正確に入力できるスプレッドシートをビジネス関係者に提供できると便利だとわかりました。コンパイル時に.propertiesファイルを生成するAntタスクを作成しました。 I18Nを簡単にします。

Springも使用している場合は、はるかに優れています。それらのMessageSourceクラスは、これらの種類のことに役立ちます。

5
duffymo

この特定の死んだ馬をむち打ち続けるために、エラーが終了することが示されているときにnumeric error codesを十分に活用しました-実際のエラーメッセージを頻繁に忘れたり誤解したりするが、実際に何が起こったのかを知る手がかりとなる数値を保持して報告する場合があるため。

4
telcopro

これを解決する方法はたくさんあります。私の好ましいアプローチは、インターフェースを持つことです:

public interface ICode {
     /*your preferred code type here, can be int or string or whatever*/ id();
}

public interface iMessage {
    ICode code();
}

メッセージを提供する列挙をいくつでも定義できるようになりました。

public enum DatabaseMessage implements iMessage {
     CONNECTION_FAILURE(DatabaseCode.CONNECTION_FAILURE, ...);
}

これで、それらを文字列に変換するいくつかのオプションができました。注釈または列挙コンストラクターのパラメーターを使用して、文字列をコードにコンパイルするか、config/propertyファイルまたはデータベーステーブルまたは混合から読み取ることができます。後者は私が好むアプローチです。なぜなら、非常に早い段階でテキストに変換できるメッセージが常に必要になるからです(つまり、whileデータベースに接続するか読む構成)。

ユニットテストとリフレクションフレームワークを使用して、インターフェイスを実装するすべてのタイプを見つけ、各コードがどこかで使用されていること、構成ファイルに予期されるすべてのメッセージなどが含まれていることを確認します。

Java https://github.com/javaparser/javaparser または Eclipseのもの のように解析できるフレームワークを使用すると、列挙が使用されている場所を確認し、未使用のものを見つけます。

2
Aaron Digulla

私(および社内の残りのチーム)は、エラーコードを返す代わりに例外を発生させることを好みます。エラーコードはどこでもチェックされ、渡される必要があり、コードの量が大きくなるとコードが読めなくなる傾向があります。

次に、エラークラスがメッセージを定義します。

PS:実際には国際化にも注意を払っています!

1
blabla999

少し遅れましたが、私は自分のためのきれいな解決策を探していました。別の種類のメッセージエラーがある場合は、単純なカスタムメッセージファクトリを追加して、後で詳細と形式を指定できるようにします。

public enum Error {
    DATABASE(0, "A database error has occured. "), 
    DUPLICATE_USER(1, "User already exists. ");
    ....
    private String description = "";
    public Error changeDescription(String description) {
        this.description = description;
        return this;
    }
    ....
}

Error genericError = Error.DATABASE;
Error specific = Error.DUPLICATE_USER.changeDescription("(Call Admin)");

編集:OK、ここで列挙型を使用するのは、特定の列挙型を永続的に変更するため、少し危険です。クラスに変更して静的フィールドを使用することをお勧めしますが、「==」を使用することはできません。だから私はそれがすべきでないことの良い例だと思う(または初期化中にのみそれを行う):)

1
pprzemek

以下の例に従ってください:

public enum ErrorCodes {
NO_File("No file found. "),
private ErrorCodes(String value) { 
    this.errordesc = value; 
    }
private String errordesc = ""; 
public String errordesc() {
    return errordesc;
}
public void setValue(String errordesc) {
    this.errordesc = errordesc;
}

};

コードで次のように呼び出します:

fileResponse.setErrorCode(ErrorCodes.NO_FILE.errordesc());
0
Chinmoy

メッセージ定数としてinterfaceを使用することは、一般的に悪い考えです。エクスポートされたAPIの一部として、クライアントプログラムに永続的にリークします。後のクライアントプログラマーがプログラムの一部としてそのエラーメッセージ(パブリック)を解析する可能性があることを誰が知っていますか。

文字列形式の変更によりクライアントプログラムが破損する可能性があるため、これをサポートするために永久にロックされます。

0
Awan Biru

エラーコード/メッセージ定義の列挙型は、i18nの懸念がありますが、それでもナイスソリューションです。実際には、2つの状況が考えられます。コード/メッセージがエンドユーザーに表示されるか、システムインテグレーターに表示されるかです。後者の場合、I18Nは必要ありません。 Webサービスはおそらく後者のケースだと思います。

0
Jimmy