Идеологически правильный setter?

Представим себе ситуацию — есть некоторый класс, например Subscriber, у которого есть приватное поле string Subscriber::email и метод, устанавливающий его значение Subscriber::setEmail(string val).

Очевидно, что можно попытаться задать через этот сеттер некорректный адрес (например, не содержащий символ @).

Вопрос: 1. Должен ли этот сеттер проверять корректность мыла?
2. Если должен, то каким образом он должен сигнализировать о некорректном вводе?
Через возвращаемое значение или через исключение?


P.S. Прошу принять во внимание следующие доводы:

+каждый метод должен быть ответственен за одно действие, а в п.1 мы пытаемся возложить на этот сеттер и проверку значения и установку поля.

+если все же примериться с этим, и заставить метод возвращать false при попытке ввода некорректного мыла, то тогда придется каждый раз писать if (!s->setEmail(«blah-blah»)) {...} — как-то это слишком, мне кажется.

+исключение нужно генерировать тогда, когда ошибку нельзя исправить в том месте, где она была обнаружена. Обычно так бывает при большой вложенности стека вызовов функций, а в нашем случае исключение кажется не совсем уместным.
  • Вопрос задан
  • 3245 просмотров
Пригласить эксперта
Ответы на вопрос 8
@gelas
Проверять корректность должен.
При этом он остается ответственным за одно действие — за установку именно email'a, а не произвольной строки.
Поэтому и генерировать исключение при неправильных данных тоже должен.
Метод же неспроста называется setEmail.
Вы же не удивляетесь, что при попытке присвоить строку в integer поле получаете ошибку (вероятно даже компиляции).
Здесь же происходит фактически тоже самое.
Если говорить про идейность, то еще лучше сделать тип Email и проверки делать в нем. Но в обычном, а не идеальном коде, это почти наверняка будет излишним. Поэтому проверка в setter'e — хороший компромисс.
Ответ написан
Комментировать
Akson87
@Akson87
Все зависит от того, где этот сеттер доступен. Есть подход с разделением безопасных и небезопасных участков. Если у Вас в приложении есть UI и данные приходят из него, тогда код пользовательского интерфейса должен проверять корректность email, а сеттер должен устанавливать то, что ему дадут. Если же этот сеттер доступен пользователю или каким-то третьим лицам, тогда надо делать эту проверку внутри сеттера и тут уж надо смотреть на то, как реакция на ошибку обычно обрабатывается другими методами. Если везде исключения, пусть будут исключения, если везде возвращение False, то можно и так. Если же этот код только для разработчиков, то можно в дебаге делать assert, а в релизе кидать большое и страшное исключение (но только в случае, если некорректные данные приходят из-за кривой логики, пользовательский ввод должен проверяться задолго до этого).
Ответ написан
Sardar
@Sardar
> каждый метод должен быть ответственен за одно действие
Не надо понимать все так буквально. Не стоит к примеру подключаться к базе, выбирать инфу, закачивать обратно результаты и сливать инфу в лог одной большой простыней в одной функции. Но это не значит, что каждую простейшую операцию (такую как присваивание) нужно выносить в отдельный метод.

> а в нашем случае исключение кажется не совсем уместным
Исключение говорит «я не могу выполнить команду», в данном случае не допускаем некорректный ввод. Не надо боятся и экономить на исключениях, это удобный механизм (вплоть до таких задач как остановить map(), но это уже вопрос вкуса). Не важно как глубоко «вложен» вызов до вашей функции/свойства, вы на самом деле этого не знаете и не можете знать.

Да, ваш сеттер должен проверять ввод и присваивать значение, если оно съедобно, иначе генерить исключение.
Ответ написан
blog.byndyu.ru/2009/10/blog-post.html
Начать читать с 2. Валидация данных.
ето для примера.
Ответ написан
Я так понимаю, у вас больше стоит вопрос когда и как проверять на валидность? Лично мне очень по душе концепция баррикад. Тем более если у вас это все глубоко в функционале. Просто фильтруйте входящие данные на ранних стадиях.
Ответ написан
А почему адрес должен быть стрингом. Пусть будет отдельным классом, с внутренней проверкой валидности.

Subscriber::setEmail(EmailAdress val)

А уже в сеттере самого класса EmailAdress сделать проверку.
Ответ написан
Eternalko
@Eternalko
> 1. Должен ли этот сеттер проверять корректность мыла?

Нет, не должен. Проверка целостности данных должна производится перед выполнением критического действия.

Однако, зачем тогда такой простой метод?

C таким же успехом можно просто писать:
$s->email=«vasia@pup.zemli»;

Сеттеры и валидаторы должны жить отдельной жизнью.
Ответ написан
Комментировать
ainu
@ainu
Рельсы делают проверку непосредственно при сохранении. И правильно делают. Ошибки передают в систему, далее они отображаются пользователю напрямую.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы