Try/Catch in Constructor - Рекомендуемая практика?

Что-то, что мне всегда было интересно

public class FileDataValidator {

private String[] lineData;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        e.printStackTrace();
    }

}

//validation methods below all throwing InvalidFormatException

Не рекомендуется включать блок try/catch внутри моего конструктора? Я знаю, что у меня мог бы Constructor вернуть Exception обратно вызывающему. Что вы, ребята, предпочитаете при вызове методов, как я делал в Constructor? В вызывающем классе вы предпочли бы создать экземпляр FileDataValidator и вызвать методы там в этом экземпляре? Просто интересно услышать отзывы!

Ответы

Ответ 1

В показанном коде проблемы проверки не возвращаются к коду, который создает этот экземпляр объекта. Вероятно, это НЕ ХОРОШЕЕ.

Вариант 1:

Если вы поймаете исключение внутри метода/конструктора, обязательно передайте что-то обратно вызывающему. Вы можете поместить поле isValid, которое получает значение true, если все работает. Это будет выглядеть так:

private boolean isValid = false;

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
        isValid = true;
    }
    catch(InvalidFormatException e)
    {
        isValid = false;
    }
}

public boolean isValid() {
    return isValid;
}

Вариант 2:

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

public FileDataValidator(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

Вариант 3:

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

String[] lineData = readLineData();
FileDataValidator onePerson = new FileDataValidator();
try {
    onePerson.build(lineData);
} catch (InvalidDataException e) {
    // What to do it its bad?
}

Вот код класса:

public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();

    try
    {
        validateName();
        validateAge();
        validateTown();
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

Конечно, функция build() может использовать метод isValid(), который вы вызываете, чтобы увидеть, правильно ли это право, но исключение, для функции сборки.

Вариант 4:

Четвертый метод, который я хочу упомянуть, - это то, что мне больше всего нравится. У этого есть такой код. В вызывающем коде вы должны вызвать конструктор, а затем вызвать функцию build(), которая либо будет работать, либо нет.

Этот вид следует за тем, как работают JaxB и JaxRS, что похоже на то, что у вас есть.

  • Внешний источник данных - у вас есть файл, у них есть входящее сообщение в формате XML или JSON.
  • Код для создания объектов - у вас есть код, у них есть свои библиотеки кода, работающие по спецификациям в различных JSR.
  • Валидация не привязана к построению объектов.

Вызывающий код:

String[] lineData = readLineData();
Person onePerson = new Person();
FileDataUtilities util = new FileDataUtilities();
try {
    util.build(onePerson, lineData);
    util.validate(onePerson);
} catch (InvalidDataException e) {
    // What to do it its bad?
}

Вот код класса, в котором живут данные:

public class Person {
    private Name name;
    private Age age;
    private Town town;
... lots more stuff here ...
}

И код утилиты для сборки и проверки:

public FileDataValidator() {
    // maybe you need some code in here, maybe not
}

public void build(Person person, String[] lineData){

    this.lineData = lineData;
    removeLeadingAndTrailingQuotes();
    setNameFromData(person);
    setAgeFromData(person);
    setTownFromData(person);
}

public boolean validate(Person person) {

    try
    {
        validateName(person);
        validateAge(person);
        validateTown(person);
        return true;
    }
    catch(InvalidFormatException e)
    {
        throw new com.myco.myapp.errors.InvalidDataException(e.getMessage());
    }

}

Ответ 2

Мое предпочтение заключается в том, что исключения обрабатываются бит кода, который знает, как с ними бороться. В этом случае я бы предположил, что бит кода, создающий FileDataValidator, знает, что должно произойти, если данные файла недействительны, и там должны быть рассмотрены исключения (я сторонник распространения на вызывающего абонента).

Во время обсуждения лучшей практики - имя класса FileDataValidator пахнет мне. Если создаваемый объект хранит данные файла, я бы назвал его FileData - возможно, с помощью метода проверки достоверности? Если вы хотите только проверить данные своего файла, тогда будет достаточно статического метода.

Ответ 3

Вы должны рассмотреть статический шаблон factory. Сделайте свой конструктор all-arguments закрытым. Предоставьте статический метод FileDataValidator (args...). Это принимает и проверяет все аргументы. Если все в порядке, он может вызвать частный конструктор и вернуть вновь созданный объект. Если что-то не удается, бросьте исключение, чтобы сообщить вызывающему, что оно предоставило плохие значения.

Я должен также упомянуть, что это: catch (Исключение e) { printSomeThing (е); }

Является самым смертоносным противником, который вы могли бы сделать с Исключениями. Да, вы можете прочитать некоторые значения ошибок в командной строке, а затем? Вызывающий (который предоставил плохие значения) не получает информацию о плохих значениях, выполнение программы будет продолжаться.