Как отмечено в комментариях @ benjamin-gruenbaum, это называется булевой ловушкой:
Скажем, у меня есть такая функция
UpdateRow(var item, bool externalCall);
и в моем контроллере это значение externalCall
всегда будет TRUE. Каков наилучший способ вызвать эту функцию? Я обычно пишу
UpdateRow(item, true);
Но я спрашиваю себя, должен ли я объявить логическое значение, просто чтобы указать, что означает это «истинное» значение? Вы можете узнать это, посмотрев объявление функции, но это, очевидно, быстрее и понятнее, если вы только что увидели что-то вроде
bool externalCall = true;
UpdateRow(item, externalCall);
PD: Не уверен, что этот вопрос действительно подходит, если нет, где я могу получить больше информации об этом?
PD2: Я не пометил ни одного языка, потому что думал, что это очень общая проблема. Во всяком случае, я работаю с C # и принятый ответ работает для C #
data CallType = ExternalCall | InternalCall
в хаскеле например.Ответы:
Не всегда есть идеальное решение, но у вас есть много вариантов на выбор:
Используйте именованные аргументы , если они доступны на вашем языке. Это работает очень хорошо и не имеет особых недостатков. В некоторых языках любой аргумент может быть передан как именованный аргумент, например
updateRow(item, externalCall: true)
( C # ) илиupdate_row(item, external_call=True)
(Python).Ваше предложение использовать отдельную переменную является одним из способов имитации именованных аргументов, но не имеет связанных с этим преимуществ безопасности (нет гарантии, что вы использовали правильное имя переменной для этого аргумента).
Используйте различные функции для вашего открытого интерфейса, с лучшими именами. Это еще один способ имитации именованных параметров путем помещения значений параметров в имя.
Это очень читабельно, но приводит к большому количеству шаблонов для вас, которые пишут эти функции. Он также не может справиться с комбинаторным взрывом, когда существует множество логических аргументов. Существенным недостатком является то, что клиенты не могут установить это значение динамически, но должны использовать if / else для вызова правильной функции.
Используйте перечисление . Проблема с логическими значениями заключается в том, что они называются «истина» и «ложь». Итак, вместо этого введите тип с лучшими именами (например
enum CallType { INTERNAL, EXTERNAL }
). Как дополнительное преимущество, это повышает безопасность типов вашей программы (если ваш язык реализует перечисления как отдельные типы). Недостаток перечислений заключается в том, что они добавляют тип в ваш общедоступный API. Для чисто внутренних функций это не имеет значения, и перечисления не имеют существенных недостатков.В языках без перечислений вместо этого иногда используются короткие строки . Это работает, и может даже быть лучше, чем необработанные логические значения, но очень подвержено опечаткам. Затем функция должна немедленно подтвердить, что аргумент соответствует набору возможных значений.
Ни одно из этих решений не оказывает чрезмерного влияния на производительность. Именованные параметры и перечисления могут быть полностью разрешены во время компиляции (для скомпилированного языка). Использование строк может включать сравнение строк, но стоимость этого незначительна для небольших строковых литералов и большинства видов приложений.
источник
Правильное решение - сделать то, что вы предлагаете, но упаковать его в мини-фасад:
Читаемость превосходит микрооптимизацию. Вы можете позволить себе дополнительный вызов функции, конечно же, лучше, чем вы можете позволить разработчику попытаться найти семантику логического флага хотя бы один раз.
источник
true
делает.) Это будет целесообразно, даже если стоит столько же времени процессора, сколько экономит время разработчика, поскольку время процессора намного дешевле.UpdateRow(item, true /*external call*/);
что будет чище, в языках, которые допускают синтаксис комментариев. Раздувание вашего кода с помощью дополнительной функции просто для того, чтобы избежать написания комментария, кажется не стоит для простого случая. Возможно, если бы к этой функции было много других аргументов и / или какой-нибудь загроможденный + хитрый окружающий код, он начал бы привлекать больше. Но я думаю, что если вы отлаживаете, вы в конечном итоге проверяете функцию-обертку, чтобы увидеть, что она делает, и должны помнить, какие функции являются тонкими обертками вокруг библиотечного API, а какие на самом деле имеют некоторую логику.Почему у вас есть такая функция?
При каких обстоятельствах вы бы назвали его с аргументом externalCall, установленным на разные значения?
Если один из, скажем, внешнего клиентского приложения, а другой находится в одной и той же программе (то есть в разных модулях кода), то я бы сказал, что у вас должно быть два разных метода, по одному для каждого случая, возможно, даже определенных для разных Интерфейсы.
Однако, если вы выполняете вызов на основе некоторой базы данных, взятой из непрограммного источника (скажем, из файла конфигурации или чтения базы данных), тогда метод логической передачи будет иметь больше смысла.
источник
Хотя я согласен, что для обеспечения удобочитаемости и безопасности значений идеально использовать языковую функцию, вы также можете выбрать практический подход: комментарии во время разговора. Подобно:
или же:
или (правильно, как предложено Фраксом):
источник
UpdateRow(item, /* externalCall */ true )
. Комментарий в полном предложении анализировать гораздо сложнее, на самом деле это в основном шум (особенно второй вариант, который также очень слабо связан с аргументом).Вы можете «назвать» ваши bools. Ниже приведен пример для языка ОО (где он может быть выражен в классе, предоставляющем
UpdateRow()
), однако сама концепция может быть применена на любом языке:и на сайте вызова:
Я считаю, что пункт № 1 лучше, но он не заставляет пользователя использовать новые переменные при использовании функции. Если безопасность типов важна для вас, вы можете создать
enum
тип и заставить егоUpdateRow()
принять вместоbool
:UpdateRow(var item, ExternalCallAvailability ec);
Вы можете изменить имя функции так, чтобы оно лучше отражало значение
bool
параметра. Не очень уверен, но возможно:UpdateRowWithExternalCall(var item, bool externalCall)
источник
externalCall=false
, ее имя не имеет абсолютно никакого смысла. Остальная часть вашего ответа хороша.UpdateRowWithUsuallyExternalButPossiblyInternalCall
.Другой вариант, который я еще не читал здесь: использовать современную среду разработки.
Например, IntelliJ IDEA печатает имя переменной переменных в вызываемом методе, если вы передаете литерал, такой как
true
илиnull
илиusername + “@company.com
. Это сделано маленьким шрифтом, чтобы он не занимал слишком много места на экране и сильно отличался от реального кода.Я до сих пор не говорю, что это хорошая идея - добавлять булевы везде. Аргумент, говорящий о том, что вы читаете код гораздо чаще, чем пишете, часто очень силен, но в данном конкретном случае он сильно зависит от технологии, которую вы (и ваши коллеги!) Используете для поддержки своей работы. С IDE это гораздо меньше проблем, чем, например, с vim.
Модифицированный пример части моей тестовой установки:
источник
2 дня и никто не упомянул полиморфизм?
Когда я клиент, желающий обновить строку с элементом, я не хочу думать о названии базы данных, URL-адресе микросервиса, протоколе, используемом для связи, или о том, является ли внешний вызов нужно будет использовать, чтобы это произошло. Прекратите выдвигать эти детали на меня. Просто возьми мой предмет и иди где-нибудь обновить строку.
Сделайте это, и это станет частью проблемы строительства. Это может быть решено многими способами. Вот один из них:
Если вы смотрите на это и думаете: «Но мой язык назвал аргументы, мне это не нужно». Хорошо, отлично, иди и используй их. Просто держите эту чепуху подальше от вызывающего клиента, который даже не должен знать, о чем говорит.
Следует отметить, что это больше, чем семантическая проблема. Это также проблема дизайна. Передайте логическое значение, и вы будете вынуждены использовать ветвление для его решения. Не очень объектно-ориентированный. Есть два или более логических значений для передачи? Желаете, чтобы ваш язык был многократным? Посмотрите, что коллекции могут делать, когда вы их вкладываете. Правильная структура данных может сделать вашу жизнь намного проще.
источник
Если вы можете изменить
updateRow
функцию, возможно, вы можете изменить ее на две функции. Учитывая, что функция принимает логический параметр, я подозреваю, что это выглядит так:Это может быть немного запах кода. Функция может иметь совершенно разное поведение в зависимости от того, на что установлена
externalCall
переменная, и в этом случае она имеет две разные обязанности. Слияние его на две функции, которые несут только одну ответственность, может улучшить читаемость:Теперь, где бы вы ни вызывали эти функции, вы можете сразу увидеть, вызывается ли внешняя служба.
Конечно, это не всегда так. Это ситуативный и дело вкуса. Рефакторинг функции, которая принимает логический параметр в две функции, обычно стоит рассмотреть, но это не всегда лучший выбор.
источник
Если UpdateRow находится в контролируемой кодовой базе, я бы рассмотрел шаблон стратегии:
Где Request представляет некоторый вид DAO (обратный вызов в тривиальном случае).
Истинный случай:
Ложный случай:
Обычно реализация конечной точки настраивается на более высоком уровне абстракции, и я бы просто использовал ее здесь. Как полезный бесплатный побочный эффект, это дает мне возможность реализовать другой способ доступа к удаленным данным, без каких-либо изменений в коде:
В целом, такая инверсия управления обеспечивает более низкую связь между вашим хранилищем данных и моделью.
источник