Подтвердить что ты не робот

Java. Как создать объект с допустимыми атрибутами?

Я занимаюсь базовым курсом Java, и я столкнулся с проблемой: как мне создать объект, только если я передал действительные параметры в конструктор?

Должен ли я делать альтернативный класс и вызывать конструктор оттуда после реализации валидации?

Или должен/я мог использовать статический метод в классе для проверки?

Какова наилучшая практика в этом случае?

4b9b3361

Ответ 1

Стандартная практика заключается в проверке аргументов в конструкторе. Например:

class Range {
  private final int low, high;
  Range(int low, int high) {
    if (low > high) throw new IllegalArgumentException("low can't be greater than high");
    this.low = low;
    this.high = high;
  }
}

Примечание: чтобы проверить, что аргументы не равны нулю, что довольно часто, вы можете использовать:

import static java.util.Objects.requireNonNull;

Constructor(Object o) {
  this.o = requireNonNull(o); //throws a NullPointerException if 'o' is null
}

ОБНОВЛЕНИЕ

Чтобы ответить на ваш конкретный комментарий о номере социального страхования. Одним из способов было бы добавить метод к классу:

//constructor
public YourClass(String ssn) {
  if (!isValidSSN(ssn)) throw new IllegalArgumentException("not a valid SSN: " + ssn);
  this.ssn = ssn;
}

public static boolean isValidSSN(String ssn) {
  //do some validation logic
}

Вызывающий код может выглядеть следующим образом:

String ssn = getSsnFromUser();
while(!YourClass.isValidSSN(ssn)) {
  showErrorMessage("Not a valid ssn: " + ssn);
  ssn = getSsnFromUser();
}
//at this point, the SSN is valid:
YourClass yc = new YourClass(ssn);

С этим дизайном вы достигли двух вещей:

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

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

Ответ 2

Я просто выбросил IllegalArgumentException в самом конструкторе:

public class MyClass {
    private int i;

    public MyClass (int i) {
        // example validation:
        if (i < 0) {
            throw new IllegalArgumentException ("i mustn't be negatve!");
        }
        this.i = i;
}

Ответ 3

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

Хотя вы можете генерировать исключения в своем конструкторе, это не всегда идеально. Если вы пишете общедоступные объекты, которые вы ожидаете использовать/повторно использовать другими, Исключения являются единственной реальной опцией для публичных конструкторов, однако такие ограничения и их результат (например, какое исключение будет выбрано) должны быть четко задокументированы в javadoc для класс.

Для внутренних классов утверждения более уместны. Как заявляет Oracle: "Утверждения... должны использоваться для проверки случаев, которые никогда не должны происходить, проверять допущения о структурах данных или принуждать ограничения к аргументам частных методов". Использование утверждений в технологии Java. Вероятно, вы должны документировать свои ожидания для этого класса, но ваше приложение должно делать какие-либо проверки заранее, а не полагаться на любые Исключения, которые вы выбрали.

Статические методы factory могут помочь немного, их преимущества немного зависят от другого вопроса: Как использовать "Статические методы factory" вместо конструкторов. Тем не менее, они не дают надежных вариантов проверки без повторного использования Исключений, когда вещи недействительны (что или возвращает нуль, что менее информативно).

Ваше идеальное решение - это шаблон Builder. Мало того, что это позволяет обеспечить большую гибкость в управлении вашими аргументами, вы можете проверять каждый из них отдельно или иметь метод проверки, который может оценивать все поля одновременно. Строитель может и должен использоваться, чтобы скрыть фактический конструктор объекта, пользуясь единственным доступом к нему и предотвращая любые нежелательные значения из когда-либо представленных, в то время как утверждения могут защищать от "строителя никогда не должны отправлять эти значения".

Ответ 4

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

Ответ 5

Один из способов убедиться, что у вас есть допустимые параметры, переданные конструктору, заключается в создании родительского класса с конструкторами, которые принимают только необходимые параметры, а затем создают подкласс, который используют ваши конечные пользователи. Если вы заставите своего пользователя называть super() и передать требуемые параметры, они должны, по крайней мере, пройти в нужные объекты данных. Что касается допустимых значений для этих параметров, то до вас, хотите ли вы включить проверку в конструктор родительского класса и исключить исключения времени выполнения или еще что-то.

Здесь приведен пример суперкласса/подкласса. Позвольте назвать суперкасс SomeShape и подкласс Triangle. Для любого объекта SomeShape вы будете вынуждать "пользователь" предоставить несколько сторон и длину стороны. Вот как...

public class SomeShape {
    private int numSides;
    private int sideLength;

    public SomeShape(int mNumSides, int mSideLength) {
        numSides = mNumSides;
        sideLength = mSideLength;
    }
}

public class Triangle extends SomeShape {
    private int height;

    public Triangle(int mNumSides, int mSideLength, int mHeight) {
        super(mNumSides, mSideLength);
        height = mHeight;
    }   
}

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

Ответ 6

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

Пример:

public class Foo {
  private Foo(int arg1, Bar arg2) {
    // guaranteed to be valid.
  }

  public static Foo construct(int arg1, Bar arg2) {
    // perform validation
    if (arg1 < 0 || arg2 == null) {
      return null;
    } else {
      return new Foo(arg1, arg2);
    }
  }
}

Использование

Foo object = Foo.construct(1, new Bar());
if (object == null) {
  // handle error here.
}

Ответ 7

Плохая практика - исключить исключение из конструктора. Вы получаете частично инициализированный объект, который, вероятно, сломает все виды контрактов.

Если конструктор недействителен для всех комбинаций входов, он очистит, чтобы создать метод factory, который выполняет проверку, и сделать конструктор закрытым. Если есть реальная возможность сбоя (то есть отказ не вызван ошибкой программирования), тогда может быть целесообразно вернуть Optional.