Нулевые проверки в конструкторе против метода обслуживания? - PullRequest
5 голосов
/ 29 ноября 2011

Это тема в одном из наших обзоров кода, я хотел бы получить больше мнений.

Допустим, я пишу сервис, который позволит мне вставлять простой объект Person в базу данных.

public class Person
{
   private String name;
   ....
}

У нас есть простой метод VerifyNotNull, который выбрасывает IllegalArgumentException.

Какой маршрут вы выберете и почему. Опция 1:

Убедитесь, что в конструкторе объекта Person не указано значение null.

public Person(String name)
{
     VerifyNotNull(name);//throws illegal arg exception if name is null
     this.name = name;
}

Вариант 2:

Разрешить построение Person с нулем, проверять не ноль при вызове addPerson.

public class PersonService
{
  public void addPerson(Person personToAdd)
  {
     VerifyNotNull(personToAdd.getName());//throws illegal arg exception
     //add code
  }
}

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

Допустимо ли создавать исключения в конструкторах?

Спасибо за вашу помощь!

Ответы [ 7 ]

10 голосов
/ 29 ноября 2011

Первый подход больше fail-fast , что увеличит вероятность того, что вы найдете источник вашей ошибки быстрее. Подумайте об этом так: если ваш журнал ошибок начинает сообщать вам, что возникло несколько ошибок, потому что вы пытаетесь добавить людей с нулевыми именами, вы захотите узнать, откуда появились нулевые имена этих людей. , право? В зависимости от того, как устроена ваша система, возможно, что человек был создан за несколько миль от того места, где он добавляется в услугу. Таким образом, вы не знаете, какое из четырех тысяч мест в вашем коде создает людей без имен.

Так что, если бы мне пришлось выбирать, я бы выбрал первый вариант.

Конечно, это зависит от вашей бизнес-модели. Если человек, имеющий нулевое имя, является совершенно законной вещью, которую нужно создать, когда вы находитесь на этапе ввода данных, и только когда вы готовитесь сохранить информацию этого человека, вы хотите убедиться, что он прошел проверку, то это другая история. В этом случае вам может даже потребоваться создать класс ValidatedPerson, который охватывает Person, но указывает на безопасный для типов способ, что метод addPerson может быть вызван, только если кто-то прошел процесс проверки, потому что единственный способ создать ValidationPerson - это специальный метод validate, который проверяет имя человека.

5 голосов
/ 29 ноября 2011

Я бы бросил исключение в Ctor.Основными причинами этого являются:

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

Если вы используете IntelliJ, вы также можете использовать JetBrain's @NotNullи аннотации @Nullable, поэтому IDE также выдаст вам предупреждение, если вы вызовете Ctor с нулевым аргументом.См http://www.jetbrains.com/idea/documentation/howto.html.

3 голосов
/ 29 ноября 2011

Ваш выбор не ограничен двумя перечисленными вами вариантами.

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

Например, можно использовать фабричный метод для достижения этого:

class Person {
    private final String name;
    private Person(String name) { // private constructor
        this.name = name;
    }
    public static Person newPerson(String name) { // factory method
        VerifyNotNull(name); // IAE not from c'tor, guaranteed check
        return new Person(name);
    }
 }

Другой способ, если вы хотите избежать статических методов, может быть с помощью Builder, как показано ниже:

class Person {
    private final String name;
    private Person(String name) { // private constructor
        this.name = name;
    }

    /**
     * Usage: Person p = new Person.Builder(name).build();
     */
    public static class Builder {
        private final String name;
        public Builder(String name) {
            this.name = name;
        }
        public Person build() {
            VerifyNotNull(name); // IAE not from c'tor, guaranteed check
            return new Person(name);
        }
    }
}
1 голос
/ 29 ноября 2011

Два разных подхода.

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

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

1 голос
/ 29 ноября 2011

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

0 голосов
/ 09 февраля 2014

Я бы сказал, что лучше выбросить исключение внутри метода обслуживания.

Причина в том, что проверки достоверности, как правило, часто зависят от бизнес-требований, специфичных для конкретного приложения;уровень обслуживания - это то место, где находится большая часть бизнес-логики для конкретного приложения (предостережение: против этого есть аргументы; см. также: «модель анемичной области»).Если вы добавите ограничение внутри класса Person, то указанный класс станет менее пригодным для повторного использования / переносимым, так как вы можете захотеть использовать класс в другом месте, который допускает свойство нулевого имени внутри этого конкретного конструктора.

0 голосов
/ 01 декабря 2011

Правило, согласно которому имя не может быть пустым, является бизнес-логикой и, следовательно, принадлежит сущности Person.Таким образом, опция конструктора является лучшей из двух.Однако вы можете использовать фабричный метод (Эванс):

public class Person
{ 
    public Person()
    {
       //Nothing much here.
    }

    public static Person Create(String name)
    {
        VerifyNotNull(name);//throws illegal arg exception if name is null
        Person person = new Person();
        person.name = name;
        return person;
    }

    ...
}
...