Хороший стиль кода для повсеместной проверки данных?

10

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

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

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

Это значительно сократило время, затрачиваемое на то, чтобы данные, которые я передаю, «вписывались» в каждую функцию, потому что сами классы и функции теперь предупреждают меня, когда какой-то ввод неверен (а иногда даже исправляет это), а я нет Я должен идти с отладчиком через весь код, чтобы выяснить, где что-то пошло не так.

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

(К вашему сведению: я все еще новичок, поэтому извините, если этот вопрос был наивным; мой проект на Python.)

user7088941
источник
1
Возможный дубликат Насколько мы должны защищаться?
комнат
3
@gnat Это похоже, но вместо того, чтобы отвечать на мой вопрос, там даются советы («будь как можно осторожнее») для конкретного экземпляра , упомянутого OP, который отличается от моего более общего запроса.
user7088941
2
«Но так как этот проект постоянно развивается, я бы потратил больше времени и больше заботился о чистом коде, чем фактически добавлял новый материал» - это звучит так, как будто вам нужно начать беспокоиться о чистом коде. В противном случае вы обнаружите, что ваша производительность замедляется и замедляется, так как каждый новый бит становится все труднее добавлять из-за существующего кода. Не все рефакторинг должен быть «завершен», если добавить что-то новое сложно из-за существующего кода, к которому он прикасается, реорганизовать только этот трогательный код и записать, что вы хотели бы вернуться позже
matt freake
3
Это проблема, с которой люди часто сталкиваются, используя слабо типизированные языки. Если вы не хотите или можете переключиться на более строго типизированный язык, ответ прост: «да, этот стиль кода подходит для решения этой проблемы» . Следующий вопрос?
Док Браун
1
В строго типизированном языке с определенными правильными типами данных компилятор сделал бы это для вас.
SD

Ответы:

4

Лучшее решение - использовать все преимущества возможностей и инструментов языка Python.

Например, в функции 1 ожидаемым вводом является массив строк, где первая строка обозначает заголовок чего-либо, а вторая - библиографическую ссылку. В функции 2 ожидаемый ввод по-прежнему является массивом строк, но теперь роли строк инвертированы.

Эта проблема смягчена с namedtuple. Это легкий и дает легкий смысловой смысл для членов вашего массива.

Чтобы воспользоваться преимуществами автоматической проверки типов без переключения языков, вы можете воспользоваться подсказками типов . Хорошая IDE может использовать это, чтобы сообщить вам, когда вы делаете что-то глупое.

Вы также, похоже, беспокоитесь о том, что функции устаревают при изменении требований. Это можно поймать с помощью автоматического тестирования .

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


источник
+1 за указание на меня namedtupleи все другие приятные вещи. Я не думал об этом namedtuple- и хотя я знал об автоматизированном тестировании, я никогда особо не использовал его и не понимал, насколько это поможет мне в этом случае. Все это действительно похоже на статический анализ. (Автоматическое тестирование может быть даже лучше, так как я могу уловить все тонкие вещи, которые не будут обнаружены при статическом анализе!) Если вы знаете что-нибудь другое, пожалуйста, дайте мне знать. Я собираюсь держать вопрос открытым еще немного, но если другие ответы не придут, я приму ваши.
user7088941
9

ОК, настоящая проблема описана в комментарии ниже этого ответа:

Например, в функции 1 ожидаемым вводом является массив строк, где первая строка обозначает заголовок чего-либо, а вторая - библиографическую ссылку. В функции 2 ожидаемый ввод по-прежнему представляет собой массив строк, но теперь роли строк поменялись местами.

Проблема здесь заключается в использовании списка строк, где порядок обозначает семантику. Это действительно подверженный ошибкам подход. Вместо этого вы должны создать собственный класс с двумя полями с именами titleи bibliographical_reference. Таким образом, вы не собираетесь смешивать их, и вы избежите этой проблемы в будущем. Конечно, это требует некоторого рефакторинга, если вы уже используете списки строк во многих местах, но, поверьте мне, это будет дешевле в долгосрочной перспективе.

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

JacquesB
источник
Иногда проблема еще более тонкая: я передаю правильный тип, но «внутренняя структура» моего ввода портит функцию: например, в функции 1 ожидаемый ввод - это массив строк, где первая строка обозначает название чего-либо, а второе - библиографическую ссылку. В функции 2 ожидаемый ввод по-прежнему представляет собой массив строк, но теперь роли строк поменялись местами: первая строка должна быть библиографической ссылкой, а вторая - библиографической ссылкой. Я думаю, для этого чеки уместны?
user7088941
1
@ user7088941: Проблема, которую вы описываете, может быть легко решена с помощью класса с двумя полями: «title» и «bibliographic_reference». Вы не перепутаете это. Опираясь на порядок в списке строк, кажется очень подверженным ошибкам. Может быть, это основная проблема?
JacquesB
3
Это ответ. Python является объектно-ориентированным языком, а не языком со списком строк-на-целых (или чем-то еще). Итак, используйте объекты. Объекты отвечают за управление своим собственным состоянием и реализацию своих собственных инвариантов, другие объекты не могут их повредить, если они правильно спроектированы. Если неструктурированные или полуструктурированные данные поступают в вашу систему извне, вы проверяете и анализируете один раз на границе системы и конвертируете в расширенные объекты как можно скорее.
Йорг Миттаг
3
«Я бы на самом деле избегал постоянного рефакторинга», - этот ментальный блок - ваша проблема. Хороший код возникает только в результате рефакторинга. Много рефакторинга. Поддерживается юнит-тестами. Особенно, когда компоненты должны быть расширены или усовершенствованы.
Док Браун
2
Я понял. +1 за все хорошие идеи и комментарии. И спасибо всем за их невероятно полезные комментарии! (В то время как я использовал некоторые классы / объекты, я перемежал их с упомянутыми списками, что, как я вижу сейчас, не было хорошей идеей. Остался вопрос, как лучше всего реализовать это, где я использовал конкретные предложения из ответа JETM , который действительно имел радикальное значение с точки зрения скорости достижения
безошибочного
3

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

Чтобы избежать необходимости всегда выяснять, что и где сломалось, я начал рассматривать каждую функцию и класс как «изолированную сущность» в том смысле, что она не может полагаться на внешний код, предоставляющий ей правильный ввод, и должна сама выполнять проверку ввода.

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

(или, в некоторых случаях, переделать данные, если данные приведены в неправильной форме).

Это может быть не очень хорошая идея. Если вы заметили, что часть вашей программы вызывает функцию с неправильно отформатированными данными, FIX THAT PART , не изменяйте вызываемую функцию, чтобы в любом случае иметь возможность переваривать неверные данные.

Это значительно сократило время, затрачиваемое на то, чтобы данные, которые я передаю, «вписывались» в каждую функцию, потому что сами классы и функции теперь предупреждают меня, когда какой-то ввод неверен (а иногда даже исправляет это), а я нет Я должен идти с отладчиком через весь код, чтобы выяснить, где что-то пошло не так.

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

Чтобы, наконец, ответить на ваш вопрос: Да, защитное программирование (то есть проверка правильности предоставленных параметров) является - в хорошей степени - хорошей стратегией. Тем не менее , как вы сказали сами, ваш код не отвечает требованиям, и я настоятельно рекомендую потратить некоторое время на рефакторинг частей, которые пахнут - вы сказали, что не хотите постоянно беспокоиться о чистом коде, тратя больше времени на «чистка», чем на новые функции ... Если вы не будете содержать в чистоте свой код, вы можете потратить вдвое больше времени на «спасение» от несоблюдения чистоты кода при устранении ошибок, и вам будет трудно реализовать новые функции - техническая задолженность может раздавить вас.

CharonX
источник
1

Это нормально. Я имел обыкновение кодировать в FoxPro, где у меня были блоки TRY..CATCH почти в каждой большой функции. Теперь я пишу код в JavaScript / LiveScript и редко проверяю параметры во «внутренних» или «частных» функциях.

«Сколько проверять» зависит от выбранного проекта / языка больше, чем от вашего навыка кода.

Майкл Квад
источник
1
Я думаю, это было ПОПРОБУЙ ... ЛОВИТЬ ... ИГНОР. Вы сделали противоположное тому, что просит ОП. ИМХО, их смысл - избегать несогласованности, в то время как ваша уверенность в том, что программа не взорвется при ударе.
Maaartinus
1
@maaartinus это правильно. Языки программирования обычно дают нам конструкции, которые просты в использовании для предотвращения взрыва приложения, но языки программирования конструкций позволяют нам предотвращать несоответствия, которые кажутся гораздо сложнее в использовании: насколько мне известно, постоянно реорганизовывать все и использовать классы, которые лучше всего контейнеризировать поток информации в вашем приложении. Это именно то, о чем я спрашиваю - есть ли более простой способ это исправить.
user7088941
@ user7088941 Вот почему я избегаю слабо типизированных языков. Python просто фантастический, но для чего-то большего, я не могу следить за тем, что я делал в другом месте. Поэтому я предпочитаю Java, которая довольно многословна (не так много с функциями Lombok и Java 8), имеет строгую типизацию и инструменты для статического анализа. Я бы посоветовал вам попробовать какой-то строго типизированный язык, так как я не знаю, как его решить иначе.
Maaartinus
Это не о строгом / свободном типизированном параметре. Речь идет о том, чтобы знать, что параметр является правильным. Даже если вы используете (целое число 4 байта), вам может потребоваться проверить, находится ли он, например, в каком-то диапазоне 0..10. Если вы знаете, что параметр всегда равен 0..10, вам не нужно проверять его. Например, у FoxPro нет ассоциативных массивов, очень сложно работать с его переменными, их областью действия и т. Д. Вот почему вы должны проверить чековую проверку ...
Майкл Квад
1
@ user7088941 Это не OO, но есть правило «быстро сбой». Каждый не приватный метод должен проверять свои аргументы и выдавать, когда что-то не так. Не пытайся поймать, не пытайся это исправить, просто взорви его до небес. Конечно, на более высоком уровне исключение регистрируется и обрабатывается. Поскольку ваши тесты обнаруживают большинство проблем заранее, и никакие проблемы не скрываются, код сходится к безошибочному решению намного быстрее, чем когда он устойчив к ошибкам.
Мааартин