Форум программистов, компьютерный форум, киберфорум
Java SE (J2SE)
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.68/47: Рейтинг темы: голосов - 47, средняя оценка - 4.68
88 / 86 / 55
Регистрация: 14.11.2015
Сообщений: 1,099

Валидация данных при конструировании объекта

28.05.2017, 10:03. Показов 9363. Ответов 4
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Есть следующий класс
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class PracticalLesson {
    private String date;
    private String topic;
    private int numberOfStudents;
 
    public PracticalLesson(String date, String topic, int numberOfStudents) {
        this.date = date;
        this.topic = topic;
        this.numberOfStudents = numberOfStudents;
    }
 
    public String getDate() {
        return date;
    }
    public void setDate(String date) {
        this.date = date;
    }
    public String getTopic() {
        return topic;
    }
    public void setTopic(String topic) {
        this.topic = topic;
    }
    public int getNumberOfStudents() {
        return numberOfStudents;
    }
    public void setNumberOfStudents(int numberOfStudents) {
        if(numberOfStudents < 0) {
            throw new IllegalArgumentException("Number of student's on a lesson can't be negative!");
        } else {
            this.numberOfStudents = numberOfStudents;
        }
    }
}
В последнем сеттере я сделал проверку на валидность данных, но она распространяется только на сеттер. То есть если я буду создавать объект через конструктор, то невалидные данные пройдут.

Как проверять валидность данных через конструктор? Вызывать сеттеры в конструкторе считается плохой практикой. Остается только дублировать код сеттера в конструкторе или есть какой-то другой выход?
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
28.05.2017, 10:03
Ответы с готовыми решениями:

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

Как создать контрол, который при конструировании не должен отображаться на форме?
Может кто подскажет, как создать контрол, который при конструировании не должен отображаться на форме, а должен находиться внизу в...

Почему при конструировании запроса не все поля видны в построителе выражений?
Спасите

4
Эксперт функциональных языков программированияЭксперт Java
 Аватар для korvin_
4576 / 2775 / 491
Регистрация: 28.04.2012
Сообщений: 8,780
28.05.2017, 11:05
Лучший ответ Сообщение было отмечено Artmal как решение

Решение

Цитата Сообщение от Artmal Посмотреть сообщение
Остается только дублировать код сеттера в конструкторе или есть какой-то другой выход?
Есть несколько выходов в зависимости от твоих предпочтений:
1) использовать иммутабельные объекты и валидацию только в конструкторе;
2) использовать метод валидации типа, лучше приватный, но тут свои нюансы;
3) сделать класс final и спокойно вызывать сеттеры в конструкторе;
4) использовать для поля numberOfStudents вместо int свой класс NonNegativeInteger и делать проверку в нём.

И наверняка есть и другие способы.

К пункту 2:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    private int validateNumberOfStudents(int numberOfStudents) {
        if(numberOfStudents < 0) {
            throw new IllegalArgumentException("Number of student's on a lesson can't be negative!");
        }
        return numberOfStudents;
    }
 
...
 
    public PracticalLesson(String date, String topic, int numberOfStudents) {
        this.date = date;
        this.topic = topic;
        this.numberOfStudents = validateNumberOfStudents(numberOfStudents);
    }
 
...
 
    public void setNumberOfStudents(int numberOfStudents) {
        this.numberOfStudents = validateNumberOfStudents(numberOfStudents);
    }
1
 Аватар для HighPredator
6045 / 2160 / 753
Регистрация: 10.12.2010
Сообщений: 6,005
Записей в блоге: 3
01.06.2017, 16:38
В качестве ответа на вопрос,
Цитата Сообщение от Artmal Посмотреть сообщение
Как проверять валидность данных через конструктор?
преложу вариант, который korvin_ не упомянул.

Никак.
Сделать лучше следующее: никакого конструктора -- создавать объект через фабрику. Это избавит вас от:
1) плохого кода в стиле вызова валидаторов внутри конструктора
2) необходимости таскать валидаторы полей внутри самого класса -- вы сможете сделать package-private (относительно фабрики и класса) валидатор-сервис, который сможете дергать как и из фабрики, так и из сеттеров
3) тесной связанности в логике (см 2)
1
Эксперт Java
3639 / 2971 / 918
Регистрация: 05.07.2013
Сообщений: 14,220
01.06.2017, 20:43
зачем вообще это делать, оставь на совести того, кто пользуется
0
Эксперт функциональных языков программированияЭксперт Java
 Аватар для korvin_
4576 / 2775 / 491
Регистрация: 28.04.2012
Сообщений: 8,780
01.06.2017, 22:13
Цитата Сообщение от xoraxax Посмотреть сообщение
зачем вообще это делать, оставь на совести того, кто пользуется
Было бы крайне неудобно пользоваться классами из java.time, например, если бы они не валидировали входные параметры при создании объектов.

Вот простой пример, почему валидация в правильном месте (например, при конструировании объекта) лучше, чем её отсутствие или наличие в неправильном месте:

Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package validation;
 
public interface Amount {
 
    Amount ZERO = new Amount() {
        @Override
        public int value() {
            return 0;
        }
        @Override
        public Amount add(Amount other) {
            return other;
        }
    };
 
    int value();
 
    Amount add(Amount other);
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package validation;
 
public final class ValidAmount implements Amount {
 
    public static Amount of(int value) {
        if (value > 0) {
            return new ValidAmount(value);
        } else if (value == 0) {
            return Amount.ZERO;
        } else {
            throw new IllegalArgumentException("negative value");
        }
    }
 
    private final int value;
 
    private ValidAmount(int value) {
        this.value = value;
    }
 
    @Override
    public int value() {
        return value;
    }
 
    @Override
    public Amount add(Amount other) {
        if (other == Amount.ZERO) {
            return this;
        }
        return of(value + other.value());
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package validation;
 
public final class InvalidAmount implements Amount {
 
    public static Amount of(int value) {
        if (value == 0) {
            return Amount.ZERO;
        }
        return new InvalidAmount(value);
    }
 
    private final int value;
 
    private InvalidAmount(int value) {
        this.value = value;
    }
 
    @Override
    public int value() {
        return value;
    }
 
    @Override
    public Amount add(Amount other) {
        if (other == Amount.ZERO) {
            return this;
        }
        return of(value + other.value());
    }
}
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package validation;
 
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.function.IntFunction;
 
public final class Main {
 
    public static void main(String[] args) {
        demo(ValidAmount::of, -5); // bug can be easily caught by stacktrace
        demo(InvalidAmount::of, -5); // hidden bug
        demo(InvalidAmount::of, -15); // bug is caught, but stacktrace is useless
    }
 
    private static void demo(IntFunction<Amount> amountConstructor, int n) {
        final BlockingQueue<Message> amounts = new ArrayBlockingQueue<>(10);
        try {
            new Thread(sum(amounts)).start();
            try { // for demo
                for (int i = 5; i > n; i--) {
                    final Amount a = amountConstructor.apply(i);
                    amounts.put(new Message(a));
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            amounts.offer(Message.END);
        }
    }
 
    private static Runnable sum(BlockingQueue<Message> amounts) {
        return () -> {
            Amount sum = Amount.ZERO;
            Message newMessage;
            try {
                while ((newMessage = amounts.take()) != Message.END) {
                    sum = sum.add(newMessage.amount);
                }
                try { // for demo
                    finalSumConsumer(sum);
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        };
    }
 
    private static void finalSumConsumer(Amount a) {
        final int amount = a.value();
        if (amount < 0) {
            throw new RuntimeException("Negative amount: " + amount);
        }
        System.out.println("Sum = " + amount);
    }
 
    private static final class Message {
        static final Message END = new Message(null);
        final Amount amount;
        Message(Amount amount) {
            this.amount = amount;
        }
    }
}
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// demo(ValidAmount::of, -5); // bug can be easily caught by stacktrace
 
java.lang.IllegalArgumentException: negative value
    at validation.ValidAmount.of(ValidAmount.java:11)
    at validation.Main.demo(Main.java:21)
    at validation.Main.main(Main.java:10)
Sum = 15
 
 
// demo(InvalidAmount::of, -5); // hidden bug
 
Sum = 5
 
 
// demo(InvalidAmount::of, -15); // bug is caught, but stacktrace is useless
 
Exception in thread "Thread-2" java.lang.RuntimeException: Negative amount: -90
    at validation.Main.finalSumConsumer(Main.java:56)
    at validation.Main.lambda$sum$0(Main.java:43)
    at java.lang.Thread.run(Thread.java:745)
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
01.06.2017, 22:13
Помогаю со студенческими работами здесь

Ошибка при запуске VS "Возникло исключение при конструировании контента этого фрейма"
Возникло исключение при конструировании контента этого фрейма. Эти сведения также регистрируются при выполнении данного приложения с...

Валидация данных при разных условиях с использованием knockoutjs
Написал правило для проверки вхождения значения в массив данных. ko.validation.rules = { validator: function (val, param) { ...

Валидация полей: вывод сообщения при некорректном вводе данных
Есть вот такой код: if ($('#iname').val().trim() == &quot;&quot; || isDidit($('#iname').val())) { $('#spanname').html(&quot;&lt;font...

Валидация json объекта
Уважаемые форумчани. Не могу написать схему для валидации следующего объекта. { &quot;id_client&quot; : &quot;123&quot;, ...

Валидация полей объекта
Доброго времени суток, форумчане. Есть класс: Produtc.cs using System.ComponentModel.DataAnnotations; public...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
5
Ответ Создать тему
Новые блоги и статьи
Жизнь в неопределённости
kumehtar 23.03.2026
Жизнь — это постоянное существование в неопределённости. Например, даже если у тебя есть список дел, невозможно дойти до точки, где всё окончательно завершено и больше ничего не осталось. В принципе,. . .
Модель здравоСохранения: работники работают быстрее после её введения.
anaschu 23.03.2026
geJalZw1fLo Корпорация до введения программа здравоохранения имела много невыполненных работниками заданий, после введения программы количество заданий выросло. Но на выплатах по больничным это. . .
1С: Контроль уникальности заводского номера
Maks 23.03.2026
Алгоритм контроля уникальности заводского (или серийного) номера на примере документа выдачи шин для спецтехники с табличной частью. Данные берутся из регистра сведений, по которому настроено. . .
Хочу заставить корпорации вкладываться в здоровье сотрудников: делаю мат модель здравосохранения
anaschu 22.03.2026
e7EYtONaj8Y Z4Tv2zpXVVo https:/ / github. com/ shumilovas/ med2. git
1С: Программный отбор элементов справочника по группе
Maks 22.03.2026
Установка программного отбора элементов справочника "Номенклатура" из модуля формы документа. В качестве фильтра для отбора справочника служит группа номенклатуры. Отбор по наименованию группы. . .
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
1С: Программный отбор элементов справочника по значению перечисления
Maks 21.03.2026
Установка программного отбора элементов справочника "Сотрудники" из модуля формы документа. В качестве фильтра для отбора служит значение перечислений. / / Событие "НачалоВыбора" реквизита на форме. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru