Лучший способ определить коды ошибок/строки в Java?

Я пишу веб-службу на Java, и я пытается найти лучший способ определить коды ошибок и связанные с ними строки ошибок. Мне нужно иметь числовой код ошибки и строку ошибки, сгруппированные вместе. И код ошибки, и строка ошибки будут отправлены клиенту, обратившись к веб-службе. Например, когда возникает 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 для этого проекта и могу документировать коды ошибок в строке и автоматически обновлять их в документации.

Ответ 1

Ну, конечно, лучшая реализация решения enum (что обычно довольно приятно):

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(), чтобы просто вернуть описание - не уверен. Во всяком случае, главное, что вам не нужно переопределять отдельно для каждого кода ошибки. Также обратите внимание, что я явно указал код вместо использования порядкового значения - это упрощает изменение порядка и добавляет/удаляет ошибки позже.

Не забывайте, что это не интернационализировано вообще, но если ваш клиент веб-службы не отправляет вам описание локали, вы не можете легко интернационализировать его самостоятельно. По крайней мере, у них будет код ошибки для i18n на стороне клиента...

Ответ 2

Насколько мне известно, я предпочитаю экрнализировать сообщения об ошибках в файлах свойств. Это будет действительно полезно в случае интернационализации вашего приложения (один файл свойств на язык). Также легче изменить сообщение об ошибке, и ему не потребуется перекомпиляция источников Java.

В моих проектах, как правило, у меня есть интерфейс, содержащий коды ошибок (String или integer, это не очень важно), который содержит ключ в файлах свойств для этой ошибки:

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.
...

Еще одна проблема с вашим решением - это поддержка: у вас всего 2 ошибки и уже 12 строк кода. Представьте себе файл Enumeration, когда у вас будет сотни ошибок для управления!

Ответ 3

Перегрузка toString() кажется немного icky - это кажется немного растянутым для использования 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; }
}

кажется намного более чистым для меня... и менее подробным.

Ответ 4

На моей последней работе я немного глубже перечислил версию:

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

@Error, @Info, @Warning сохраняются в файле класса и доступны во время выполнения. (У нас было еще несколько аннотаций, чтобы помочь описать доставку сообщений)

@Text представляет собой аннотацию времени компиляции.

Я написал для этого обработчик аннотации, который сделал следующее:

  • Убедитесь, что нет повторяющихся номеров сообщений (часть перед первым подчеркиванием)
  • Синтаксис - проверьте текст сообщения
  • Сгенерируйте файл messages.properties, содержащий текст, на который вводится значение перечисления.

Я написал несколько подпрограмм утилиты, которые помогли лог-ошибки, обернули их как исключения (при желании) и т.д.

Я пытаюсь заставить их открыть исходный код... - Скотт

Ответ 5

Я бы порекомендовал вам взглянуть на java.util.ResourceBundle. Вы должны заботиться о I18N, но это того стоит, даже если вы этого не сделаете. Внешняя реклама - очень хорошая идея. Я обнаружил, что было полезно иметь возможность отправлять электронные таблицы деловым людям, которые позволяли им указывать точный язык, который они хотели видеть. Мы создали задачу Ant для генерации файлов .properties во время компиляции. Это делает I18N тривиальным.

Если вы также используете Spring, тем лучше. Их класс MessageSource полезен для такого рода вещей.

Ответ 6

Просто, чтобы портить эту особую мертвую лошадь, мы хорошо использовали числовые коды ошибок, когда ошибки показываются конечным пользователям, поскольку они часто забывают или неправильно считывают фактическое сообщение об ошибке, но могут иногда сохраняют и сообщают числовое значение, которое может дать вам ключ к тому, что на самом деле произошло.

Ответ 7

I (и остальная часть нашей команды в моей компании) предпочитают делать исключения вместо того, чтобы возвращать коды ошибок. Коды ошибок должны проверяться повсюду, проходить вокруг и, как правило, сделать код нечитаемым, когда количество кода становится больше.

Тогда класс ошибки будет определять сообщение.

PS: и на самом деле также заботитесь о интернационализации!
PPS: вы также можете переопределить метод повышения и добавить, если потребуется, ведение журнала, фильтрацию и т.д. (по крайней мере, в средах, где классы и друзья Exception можно расширять или изменять)

Ответ 8

Немного поздно, но я просто искал красивое решение для себя. Если у вас есть другая ошибка сообщения, вы можете добавить простое пользовательское сообщение factory, чтобы вы могли указать более подробную информацию и формат, который вам нужен позже.

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)");

EDIT: ok, использование enum здесь немного опасно, так как вы постоянно изменяете конкретный enum. Я думаю, лучше было бы перейти на класс и использовать статические поля, но больше не использовать '=='. Поэтому я думаю, что это хороший пример, что не делать (или делать это только во время инициализации):)

Ответ 9

Существует много способов решить эту проблему. Мой предпочтительный подход состоит в том, чтобы иметь интерфейсы:

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, ...);
}

Теперь у вас есть несколько вариантов превратить их в строки. Вы можете скомпилировать строки в свой код (используя аннотации или параметры конструктора перечисления), или вы можете прочитать их из файла конфигурации/свойства или из таблицы базы данных или смеси. Последнее - мой предпочтительный подход, потому что вам всегда понадобятся сообщения, которые вы можете превратить в текст очень рано (т.е. Когда вы подключаетесь к базе данных или читаете конфигурацию).

Я использую модульные тесты и фреймворки отражения, чтобы найти все типы, реализующие мои интерфейсы, чтобы убедиться, что каждый код используется где-то и что файлы конфигурации содержат все ожидаемые сообщения и т.д.

Использование фреймворков, которые могут анализировать Java как https://github.com/javaparser/javaparser или один из Eclipse, вы даже можете проверить, где используются перечисления и найти неиспользуемые.

Ответ 10

перечисление для определения кода ошибки/сообщения по-прежнему является хорошим решением, хотя оно имеет проблемы с i18n. На самом деле у нас могут быть две ситуации: код/​​сообщение отображается конечному пользователю или системному интегратору. Для более позднего случая I18N не требуется. Я думаю, что веб-службы, скорее всего, станут более поздними.

Ответ 11

Использование interface, поскольку константа сообщения, как правило, плохая идея. Он будет протекать в клиентской программе постоянно как часть экспортированного API. Кто знает, что позднее клиентские программисты могут анализировать сообщения об ошибках (public) как часть своей программы.

Вы будете заблокированы навсегда, чтобы поддержать это, так как изменения в строчном формате будут/могут нарушать клиентскую программу.

Ответ 12

Пожалуйста, следуйте приведенному ниже примеру:

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());