Какое поведение вы ожидаете/используете при инициализации объектов?

kudashevs

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

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

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

Вопроса тут возникает два:
- какое поведение вы ожидаете в идеале (например, вы являетесь владельцем какой-то системы и используете данных сервис в качестве зависимости в приложении)?
- как на самом деле обстоят дела в реальности и какое поведение вы реализуете в своих проектах (при условии что надо вчера и времени на вылизывание кода нет)?

P.S. Буду очень признателен если при ответе вы потратите дополнительные 2-3 минуты и укажете, каким из принципов или какими принципами/техниками (например, инкапсуляция или так требует защитное программирование) вы пользуетесь принимая то или иное решение. Если еще поделитесь ссылками на какие-то запомнившиеся материалы или подскажете что дополнительно почитать, будет вообще шикарно :)
 

MiksIr

miksir@home:~$
не передавать опции массивом
разделить сервисы на несколько, ибо явно там зреет какой-то супер-класс

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

kudashevs

Новичок
не передавать опции массивом
разделить сервисы на несколько, ибо явно там зреет какой-то супер-класс
Иногда большое количество опций все же не являются запахом. Можно посмотреть классы в Illuminate\Hashing в Laravel.

а чем по трудозатратам отличается проверка опции в конструкторе по сравнению с проверкой опции в месте использования?
По трудозатратам второе явно трудозатратнее и опаснее. Конструктор он у класса один, мест испоьзования потенциально может быть бесконечное множество и можно случайно пропустить какое-нибудь из них. Именно поэтому по уму надо делать всё в конструкторе.
 

kudashevs

Новичок
Посмотрел. Где там большое число опций в конструкторе? 4?
Пробежался поиском по разным исходникам, Monolog\Handler\PHPConsoleHandler.php весьма примечателен в смысле количества опций. И ведь не скажешь, что там прямо что-то сильно нарушено и надо его срочно разделять (хотя как всегда, всё дискусионно). В Symfony так же встречается местами. В общем мое скромное мнение, что иногда без большого количества опций физически не получается обойтись.
 

Yoskaldyr

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

AnrDaemon

Продвинутый новичок
По трудозатратам второе явно трудозатратнее и опаснее
Бред собачий.
Проверять надо там, где от этой проверки что-то зависит.

Да, 4. Может быть я в чем-то заблуждаюсь но больше 3х лично для меня уже достаточное количество чтобы начать присматриваться к чему-то повнимательнее.
Ограничивать себя с потолка взятыми числами - пусть к формализму и дутому коду.
 
Последнее редактирование:

Фанат

oncle terrible
Команда форума
Я считаю что в соответствии с направлением, в котором движется пхп, надо все проверять, и если не то, то падать быстро и громко.
Если не в конструкторе, то сделать класс, который работает с этими ёпциями, и обращаться не к опции напрямую, а к методу класса. тогда и проверка будет снова в одном месте.
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Вспоминается конечно, старая байка:

Меня два раза спрашивали [члены Парламента]: «Скажите на милость, мистер Бэббидж, что случится, если вы введете в машину неверные цифры? Cможем ли мы получить правильный ответ?» Я не могу себе даже представить какая путаница в голове может привести к подобному вопросу.
—Чарльз Бэббидж
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Ну то есть, человек, занимающийся программированием, на полном серьезе задает вопрос: "Что вы делаете, когда видите, что в вашей реализации алгоритма есть неверное или неопределенное поведение? Как именно вы пытаетесь его проигнорировать?"
 

kudashevs

Новичок
Ну то есть, человек, занимающийся программированием, на полном серьезе задает вопрос: "Что вы делаете, когда видите, что в вашей реализации алгоритма есть неверное или неопределенное поведение? Как именно вы пытаетесь его проигнорировать?"
Специально было подчеркнуто: От этого сервиса зависит дальнейшая работа какой-то большой системы, при этом мы точно знаем, что при дефолтном поведении система отработает как надо, однако не совсем так как ожидает пользователь (самый простой пример, письмо все таки отправится, но не с тем заголовком). Но ничего критичного для нас при дефолтном поведении не произойдет и убытков мы не понесем.

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

Yoskaldyr

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

WMix

герр M:)ller
Партнер клуба
я не смешивал бы интерфейс пользователя и некий сервис. с API сервиса работает машина, и она все всегда делает правильно или это логическая ошибка 500.
а пользователь может ошибаться (400), но это совсем другое и на другом уровне
 

флоппик

promotor fidei
Команда форума
Партнер клуба
Специально было подчеркнуто: От этого сервиса зависит дальнейшая работа какой-то большой системы, при этом мы точно знаем, что при дефолтном поведении система отработает как надо, однако не совсем так как ожидает пользователь (самый простой пример, письмо все таки отправится, но не с тем заголовком). Но ничего критичного для нас при дефолтном поведении не произойдет и убытков мы не понесем.
Я не очень понимаю, что эти слова значат. Для меня фраза "письмо отправится не с тем заголовком" не значит "работает как задумано". Для меня это натурально "если мы ввели неправильные числа, даст ли машина правильный ответ?"
Почему нельзя сделать так, что бы письмо отправилось с правильным заголовком? Как вообще проверять на правильность алгоритм работы, который делает что-то другое, не то, что ожидаешь?
 

WMix

герр M:)ller
Партнер клуба
@флоппик мы решили что по дефолту это донат, если пользователь не сказал за что платит.. вроде нормально
 

kudashevs

Новичок
я не смешивал бы интерфейс пользователя и некий сервис. с API сервиса работает машина, и она все всегда делает правильно или это логическая ошибка 500.
а пользователь может ошибаться (400), но это совсем другое и на другом уровне
Не имелось ввиду API, имеется ввиду что вы ставите в виде зависимости какой-то пакет в свой сервис. Если вы косячите с указанием необязательных параметров, какое поведение лично вы ожидает от класса/пакета? Если сократить варианты ответов до двух: - fast-fail или молчаливое исполнение.
 

kudashevs

Новичок
Я не очень понимаю, что эти слова значат. Для меня фраза "письмо отправится не с тем заголовком" не значит "работает как задумано". Для меня это натурально "если мы ввели неправильные числа, даст ли машина правильный ответ?"
Почему нельзя сделать так, что бы письмо отправилось с правильным заголовком? Как вообще проверять на правильность алгоритм работы, который делает что-то другое, не то, что ожидаешь?
По поводу письма, есть необязательное для заполнения поле массив, содержащее в виде ключей необязательный уточняющий заголовок письма, стиль оформления письма, еще что нибудь. Инстанциируя этот класс, пользователь вместо массива просто передал в опции строку (ну бывает, забыл). Задача сервиса отправить письмо и если мы хотим, мы можем эту задачу выполнить, не зависимо от того, правильно ли пользователь ввел данные. Вопрос, в данном случае какое поведение лично вы ожидает от класса/пакета? Если сократить варианты ответов до двух: - fast-fail или молчаливое исполнение.
 
Сверху