Я знаю, что это может быть очень специфично для конкретного случая, но я слишком часто задаюсь этим вопросом. Есть ли вообще предпочтительный синтаксис.
Я не спрашиваю, каков наилучший подход, когда в функции, я спрашиваю, должен ли я выйти рано или просто не вызывать функцию.
Обтекание, если вокруг вызова функции
if (shouldThisRun) {
runFunction();
}
Есть ли ( охранник ) в функции
runFunction() {
if (!shouldThisRun) return;
}
Последний вариант, очевидно, может уменьшить дублирование кода, если эта функция вызывается несколько раз, но иногда неправильно добавлять ее сюда, так как тогда вы можете потерять функцию единственной ответственности .
Вот пример
Если у меня есть функция updateStatus (), которая просто обновляет статус чего-либо. Я только хочу обновить статус, если статус изменился. Я знаю места в моем коде, где статус может измениться, и я знаю другие места, где он вызывающе изменился.
Я не уверен, что это только я, но мне кажется несколько грязным проверить эту внутреннюю функцию, так как я хочу сохранить эту функцию как можно более чистой - если я ее вызываю, я ожидаю, что статус будет обновлен. Но я не могу сказать, лучше ли обернуть звонок в чек, в тех немногих местах, где я знаю, есть вероятность того, что он не изменился.
Ответы:
Обертывание if вокруг вызова функции:
это для того, чтобы решить, должна ли функция вызываться вообще, и является ли она частью процесса принятия решений в вашей программе.
Защитное предложение в функции (досрочное возвращение):
это защищает от вызова с недопустимыми параметрами
Охранное предложение, используемое таким образом, сохраняет функцию «чистой» (ваш термин). Он существует только для того, чтобы функция не порвала с ошибочными входными данными.
Логика о том, вызывать ли функцию вообще, находится на более высоком уровне абстракции, даже если она просто. Он должен существовать вне самой функции. Как говорит DocBrown, у вас может быть промежуточная функция, которая выполняет эту проверку, чтобы упростить код.
Это хороший вопрос, который нужно задать, и он входит в набор вопросов, которые приводят к распознаванию уровней абстракции. Каждая функция должна работать на одном уровне абстракции, и наличие логики программы и логики функции в функции кажется вам неправильным - это потому, что они находятся на разных уровнях абстракции. Сама функция является более низким уровнем.
Соблюдая их отдельно, вы гарантируете, что ваш код будет легче писать, читать и поддерживать.
источник
У вас может быть как функция, которая не проверяет параметры, так и другая, которая делает это, например, (возможно, возвращает некоторую информацию о том, был ли выполнен вызов):
Таким образом, вы можете избежать дублирования логики, предоставляя повторно используемую
tryRunFunction
и сохраняющую оригинальную (возможно, чистую) функцию, которая не выполняет проверку внутри.Обратите внимание, что иногда вам понадобится функция, например,
tryRunFunction
с интегрированной проверкой, чтобы вы могли интегрировать проверку вrunFunction
. Или вам больше не нужно повторно использовать чек где-нибудь в вашей программе, и в этом случае вы можете оставить его в вызывающей функции.Тем не менее, постарайтесь сделать так, чтобы получателю было понятно, что происходит, давая вашим функциям собственные имена. Таким образом, вызывающим сторонам не нужно угадывать или заглядывать в реализацию, если они должны делать проверки самостоятельно или если вызываемая функция уже делает это для них. Для этого
try
может быть достаточно простого префикса like .источник
runFunction
. Функция likeupdateStatus()
может сопровождаться другой функцией likeupdateIfStatusHasChanged()
. Но это зависит от случая в 100%, для этого не существует единого решения, так что да, я согласен, идиома «попробуй» не всегда хороший выбор.Что касается того, кто решает, бежать ли, ответ от GRASP , кто является "экспертом по информации", который знает.
Как только вы решили, рассмотрите переименование функции для ясности.
Примерно так, если функция решит:
Или, если звонящий должен решить:
источник
Я бы хотел расширить ответ @ Baldrickk.
Там нет общего ответа на ваш вопрос. Это зависит от значения (контракта) вызываемой функции и характера условия.
Итак, давайте обсудим это в контексте вашего примера вызова
updateStatus()
. Это контракт, вероятно, заключается в обновлении некоторого статуса, потому что произошло что-то, влияющее на статус. Я ожидаю, что вызовы этого метода будут разрешены, даже если нет реального изменения статуса, и будут необходимы, если есть реальные изменения.Таким образом, вызывающий сайт может пропустить вызов,
updateStatus()
если он знает, что (внутри своего домена) ничего существенного не изменилось. Это ситуации, когда вызов должен быть окружен соответствующейif
конструкцией.Внутри
updateStatus()
функции могут быть ситуации, когда эта функция обнаруживает (из данных внутри своего домена), что ничего не нужно делать, и это то, куда она должна вернуться рано.Итак, вопросы:
С
updateStatus()
функцией я бы ожидал увидеть оба: вызывающие сайты, которые ничего не знают, пропускают вызов, и реализация проверяет ситуации «ничего не изменилось» на ранней стадии, даже если таким образом одно и то же условие проверяется дважды, оба внутри и снаружи.источник
Есть много хороших объяснений. Но я хочу выглядеть необычно: предположим, что вы используете этот способ:
И вам нужно вызвать другую функцию в
runFunction
методе, как это:Что ты бы сделал? Копируете ли вы все проверки сверху вниз?
Я так не думаю. Таким образом, я обычно делаю один и тот же подход: проверяю входные данные и проверяю условия в
public
методе. Публичные методы должны выполнять свои собственные проверки и проверять необходимые условия даже для вызывающих. Но пусть частные методы просто занимаются своим делом . Некоторые другие функции могут вызыватьсяrunFunction
без какой-либо проверки или проверки каких-либо условий.источник