Вопрос о стиле программирования о том, как кодировать функции

Так, сегодня я просто кодировал немного, и я понял, что у меня нет большой непротиворечивости когда дело доходит до стиля кодирования при программировании функций. Одна из моих основных проблем - то, действительно ли ее надлежащее для кодирования его так, чтобы Вы проверили, что вход пользователя допустим ЗА ПРЕДЕЛАМИ функции или просто бросает значения, переданные пользователем в функцию и проверку, если значения допустимы там. Позвольте мне делать набросок примера:

У меня есть функция, которая перечисляет хосты на основе среды, и я хочу смочь разделить среду на блоки хостов. Таким образом, пример использования - это:

listhosts -e testenv -s 2 1

Это получит все хосты от "testenv", разделит его на две части, и он отображает часть первую.

В моем коде у меня есть функция, что Вы передаете его в списке, и он возвращает список списков на основе Вас параметры для разделения. НО, прежде чем я передаю его список, я сначала проверяю параметры в своем ОСНОВНОМ во время процесса getops, так в основном я проверяю, чтобы удостовериться, что нет никаких отрицательных сторон, переданных пользователем, я удостоверяюсь, что пользователь не запрашивал разделить на, говорят, 4 части, но просьба отобразить часть 5 (который не был бы допустим), и т.д.

tl; доктор: Вы проверили бы законность, пользователи вводят поток, Вы - ОСНОВНОЙ класс, или Вы сделали бы регистрацию Вашей функции самой, и или возвратили бы допустимый ответ в случае допустимого входа или возвратили бы ПУСТОЙ УКАЗАТЕЛЬ в случае недопустимого входа?

Очевидно, обе работы методов, мне просто интересно получать известие от экспертов, относительно которых подход лучше :) Спасибо за любые комментарии и предложения Вы парни имеют! К вашему сведению мой пример кодируется в Python, но я еще больше интересуюсь общим ответом программирования в противоположность определенному для языка!

8
задан shawnjan 18 April 2010 в 22:47
поделиться

6 ответов

Хороший вопрос! Мой главный совет - подходите к проблеме систематически.Если вы разрабатываете функцию f , вот как я думаю о ее спецификации:

  1. Каким абсолютным требованиям должен соответствовать вызывающий f ? Эти требования являются f предварительным условием .

  2. Что f делает для вызывающего абонента? Когда f возвращается, каково возвращаемое значение и каково состояние машины? При каких обстоятельствах f вызывает исключение и какое исключение возникает? Ответы на все эти вопросы составляют постусловие f .

    Предусловие и постусловие вместе составляют f контракт с вызывающими абонентами. Только вызывающий абонент, удовлетворяющий предварительному условию, может полагаться на постусловие.

  3. Наконец, что касается вашего вопроса, что произойдет, если вызывающий f не удовлетворяет предварительному условию? У вас есть два варианта:

    • Вы гарантируете остановку программы, можно надеяться с информативным сообщением. Это проверенная ошибка времени выполнения.

    • Все идет. Возможно, произошла ошибка сегментации, возможно, повреждена память, может быть, f молча возвращает неправильный ответ. Это непроверенная ошибка времени выполнения.

    Обратите внимание на некоторые элементы, которых нет в этом списке: создание исключения или возврат кода ошибки. Если на такое поведение можно положиться, оно становится частью контракта f .

Теперь я могу перефразировать ваш вопрос:

Что должна делать функция, когда ее вызывающая сторона нарушает ее контракт?

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

В каждой программе не отмечен ошибки времени выполнения должны быть редкими . Неконтролируемая ошибка времени выполнения обычно оправдывается только соображениями производительности, и даже тогда только тогда, когда код критичен к производительности. Другой источник неконтролируемых ошибок времени выполнения - программирование на небезопасных языках; например, в C нет способа проверить, действительно ли указанная память инициализирована.

Другой аспект вашего вопроса:

Какие виды контрактов лучше всего подходят для дизайна?

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

У меня есть функция, которую вы передаете в виде списка, и она возвращает список списков на основе ваших параметров для разделения. НО, прежде чем передать ему список, я сначала проверяю параметры в MAIN во время процесса getops, поэтому в основном я проверяю, нет ли отрицательных значений, переданных пользователем, я убеждаюсь, что пользователь не запросил разделение на скажем, 4 части, но просят вывести часть 5.

Я думаю, что это совершенно правильный способ решить эту конкретную проблему:

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

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

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

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

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

  • parse / validate / reject с сообщением об ошибке

  • parse / validate / process

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

data Request -- type of a request
parse    :: UserInput -> Request            -- has a somewhat permissive precondition
validate :: Request -> Maybe ErrorMessage   -- has a very permissive precondition
process  :: Request -> Result               -- has a very restrictive precondition

Конечно, есть много других способов сделать это. Сбои могут быть обнаружены как на этапе синтаксического анализа, так и на этапе проверки. «Действительный запрос» на самом деле может быть представлен другим типом, чем «непроверенный запрос». И так далее.

5
ответ дан 5 December 2019 в 10:01
поделиться

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

2
ответ дан 5 December 2019 в 10:01
поделиться

Часто имеет смысл проверить ввод в обоих местах.

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

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

2
ответ дан 5 December 2019 в 10:01
поделиться

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

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

Если вы сделаете это вне функции, вы рискуете, что клиент не выполнит проверки. Метод не должен полагаться на то, что другие знают, как его правильно использовать.

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

5
ответ дан 5 December 2019 в 10:01
поделиться

Как обрабатывать ошибки, зависит от языка программирования; однако при написании приложения с командной строкой командная строка действительно должна подтверждать правильность введенных данных. Если ввод не является разумным, подходящим поведением будет печать сообщения «Использование» с объяснением требований, а также выход с ненулевым кодом состояния, чтобы другие программы знали, что это не удалось (путем тестирования кода выхода) .

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

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

1
ответ дан 5 December 2019 в 10:01
поделиться

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

2
ответ дан 5 December 2019 в 10:01
поделиться
Другие вопросы по тегам:

Похожие вопросы: