Как назвать метод, который одновременно выполняет задачу и возвращает логическое значение в качестве состояния?

33

Если есть метод

bool DoStuff() {
    try {
        // doing stuff...
        return true;
    }
    catch (SomeSpecificException ex) {
        return false;
    }
}

это лучше назвать IsStuffDone()?

Пользователь может неверно истолковать оба имени: если имя такое, DoStuff()почему оно возвращает логическое значение? Если имя IsStuffDone()такое, неясно, выполняет ли метод задачу или только проверяет ее результат.

Есть соглашение для этого случая? Или альтернативный подход, так как этот считается ошибочным? Например, в языках с выходными параметрами, таких как C #, логическая переменная состояния может быть передана методу как единое целое, а тип возвращаемого значения метода будет void.

РЕДАКТИРОВАТЬ: В моей конкретной проблеме обработка исключений не может быть напрямую делегирована вызывающей стороне, потому что метод является частью реализации интерфейса. Следовательно, вызывающей стороне не может быть поручено иметь дело со всеми исключениями различных реализаций. Он не знаком с этими исключениями. Однако вызывающая сторона может иметь дело с пользовательским исключением, StuffHasNotBeenDoneForSomeReasonExceptionкак было предложено в ответе и комментарии npinti .

Limbo Exile
источник
2
Это зависит от использования функции и того, что делается. Если необходимо, чтобы материал выполнялся правильно, я бы посчитал этот подход некорректным, поскольку пользователь функции может пропустить логический флаг, а также отсутствует информация, предоставленная исключением.
Бенни
7
В большинстве случаев я бы назвал это «сломанным», поскольку возвращение booleanвместо переноса или передачи исключения почти всегда неверно.
Maaartinus
2
Этот тип обработчика исключений редко является хорошей идеей. Улавливайте только ожидаемые исключения, а не все. И если возможно, избегайте бросать его в первую очередь, исключая необходимость ловить его.
CodesInChaos
2
Ммм ... BadlyDesignedMethodInSeriousNeedOfRefactoring? И чтобы ответить на ваш вопрос об исключениях - я бы либо позволил вызывающей стороне обработать их, либо перехватил бы их, а затем выдал пользовательское исключение, которое означает «этот метод не выполняет свою работу». Поделитесь и наслаждайтесь.
Боб Джарвис - Восстановить Монику
5
Всем тем, кто говорит: просто выбросьте (или пропустите) исключение, вы делаете необоснованные предположения о том, как этот код используется. Один из возможных сценариев состоит в том, что существует проблема, которая должна быть решена с помощью различных эвристических методов решения, которые решают все более крупные подклассы проблемы при все возрастающей стоимости; было бы смысл написать что-то вроде if (FirstMethodSucceeds(problem) or SecondMethodSucceeds(problem) or ...) Hurray(); else UniversalSolve(problem);. Делать то же самое с (пользовательскими?) Исключениями было бы бесполезно сложнее.
Марк ван Леувен

Ответы:

68

В .NET у вас часто есть пары методов, один из которых может вызвать исключение ( DoStuff), а другой возвращает логическое состояние и, при успешном выполнении, фактический результат с помощью параметра out ( TryDoStuff).

(Microsoft называет это «шаблоном Try-Parse» , поскольку, возможно, наиболее ярким примером этого являются TryParseметоды различных примитивных типов.)

Если Tryпрефикс редко встречается в вашем языке, то вам, вероятно, не следует его использовать.

ne2dmar
источник
3
+1 Это то, как я видел подобные вещи в других местах. if (TryDoStuff()) print("Did stuff"); else print("Could not do stuff");на мой взгляд, довольно стандартная и интуитивно понятная идиома.
Карл Николл
12
Следует отметить, что TryDoStuffметоды, как предполагается, терпят неудачу чисто и не имеют побочного эффекта, когда они возвращают false.
Триллиан
К вашему сведению, Removeв .NET Framework также есть методы, например, которые удаляют элемент из структуры данных (например Dictionary) и возвращают a bool. Потребитель API может решить использовать его или игнорировать. Тем не менее, ошибки сообщаются как исключения.
Омер Икбал
18

Что если вы просто сгенерировали исключение для вызывающего кода?

Таким образом, вы делегируете обработку исключений тому, кто когда-либо использует ваш код. Что делать, если в будущем вы хотели бы сделать следующее:

  • Если нет исключений, примите меры A
  • Если (например) FileNotFoundExceptionвыброшено, предпримите действие B
  • Если выдается любое другое исключение, примите меры C

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

npinti
источник
1
Что делать, если исключение имеет вид, который не может быть делегирован и должен рассматриваться внутри страны?
Limbo Exile
2
@LimboExile: Я думаю, что вы все еще могли бы иметь дело с этим внутренне, а затем, может быть, выбросить другое исключение, возможно, ваше собственное. Это иллюстрирует, что что-то, что не должно было случиться, имело место, но в то же время вы не раскрыли, что на самом деле происходит под капотом, что, как я полагаю, является причиной того, что вы хотите иметь дело с исключением внутри страны.
npinti
6
Возможно, стоит иметь в виду, что исключение должно быть исключительным случаем . Если это обычно или нормально для DoStuffсбоя, создание исключения для случая сбоя было бы сродни контролю потока программ с исключениями, что плохо. Исключения также имеют внутреннюю стоимость производительности. Если DoStuffсбой является редким случаем из-за ошибки, то исключения, безусловно, являются тем путем, который предлагает @npinti.
Карл Николл
1
@KarlNicoll Является ли что-то «исключительным», совершенно субъективно. Основным критерием должно быть то, хотите ли вы, чтобы программа аварийно завершала работу, если никто ничего не сделал с ошибкой, и хотите ли вы, чтобы вероятность ошибки присутствовала в типе функции. Кроме того, это не факт, что исключения являются дорогостоящими; это зависит от языка и от того, как вы их бросаете. Исключения дешевы в Python, и если вы удалите информацию трассировки стека, они могут быть дешевыми и в Java. Даже если затраты на производительность были слишком высоки, беспокоиться об этом преждевременно, пока вы не профилируете.
Довал
1
@Doval - я согласен, что это субъективно, поэтому OP не должна полностью игнорировать ответ npinti, так как это вполне может быть лучшим вариантом действий. Моя точка зрения заключалась в том, что выбрасывать исключения не всегда лучший путь. Есть много случаев, когда сбой не оправдывает создание исключения и потенциальный сбой приложения. Например, если DoStuff()метод OP очищается после того, как внутренний код выдает исключение, а условия Post метода все еще верны, код возврата может быть лучшим вариантом, поскольку ошибка была обработана.
Карл Николл
7

DoStuff() достаточно, и возвращаемое значение функции должно быть задокументировано, и вам не нужно упоминать его в имени функции, ища многие доступные API:

PHP

// this method update and return the number affected rows 
// called update instead of updateAndGetAffectedCount
$affected = $query->update(/* values */);

C-Sharp

// Remove item from the List
// returns true if item is successfully removed; otherwise, false.
public bool Remove( T item )
драм
источник
6

В Java API-интерфейс Collection определяет метод add, который возвращает логическое значение. Это в основном возвращает, изменило ли дополнение коллекцию. Поэтому для a Listон обычно возвращает значение true, поскольку элемент был добавлен, тогда как для a Setон может возвращать значение false, если элемент уже был там, поскольку Sets допускает каждый уникальный элемент не более одного раза.

Тем не менее, если вы добавите элемент, который не разрешен в коллекции, например, нулевое значение, добавление будет выдавать NullPointerExceptionвместо возврата false.

Когда вы применяете ту же логику к вашему случаю, зачем вам возвращать логическое значение? Если это скрыть исключение, не надо. Просто брось исключение. Таким образом, вы можете увидеть, что пошло не так. Если вам нужен логический тип, потому что он не мог быть выполнен, по какой-то другой причине, кроме исключения, назовите метод DoStuff(), поскольку он представляет поведение. Это делает вещи.

mrjink
источник
1

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

boolean doStuff() - returns true when successful

Важно документировать, что означает логическое значение.

user136354
источник
1

У меня обычно есть методы, возвращающие OperationResult(или OperationResult<T>) и устанавливающие IsSuccessсвойство OperationResultсоответствующим образом. Если «обычный» метод void, вернитесь OperationResult, если «обычный» метод возвращает объект, вернитесь OperationResult<T>и установите Itemсвойство OperationResultсоответствующим образом. Я также предложил бы иметь методы, OperationResultтакие как .Failed(string reason)и .Succeeded(T item). OperationResultбыстро становится привычным типом в ваших системах, и разработчики узнают, как с этим справиться.

Скотт Рикман
источник
0

Это действительно зависит от языка, который вы используете. Как и для некоторых языков, существуют соглашения и протоколы, которые на самом деле не существуют во многих языках или вообще отсутствуют.

Например, в языке Objective-C и именах методов iOS / Mac OS X SDK обычно используются следующие строки:

- (returndatatype) viewDidLoad {
- (returndatatype) isVisible {
- (returndatatype) appendMessage:datatype variablename with:datatype variablename {

Как видите, есть метод с именем viewDidLoad. Я предпочитаю использовать этот тип именования, когда создаю свои собственные приложения. Это можно использовать, чтобы проверить, должно ли что-то происходить, как вы сказали. Я считаю, что вы слишком глубоко думаете о том, что вы называете ваши методы. Большинство методов возвращают статус того, что они делают независимо, например:

В PHP mysql_connect () вернет false в случае сбоя соединения. Это не говорит, что это будет, но это делает, и документация говорит, что это делает так, что это не может быть неправильно истолковано программисту.

Gergy008
источник
Ах, но viewDidLoad - это скорее уведомление об обратном вызове. Пользователи не называют это, чтобы загрузить представление. Скорее, он вызывается после загрузки представления. Аналогично: isVisible не устанавливает видимость, скорее он просто возвращает текущее значение. Это ничего не делает .
lilbyrdie
0

Я хотел бы предложить использовать префикс «Try». Это хорошо известный шаблон для ситуаций, когда метод может быть успешным или нет. Этот шаблон именования использовался Microsoft в .NET Framework (например, TryParseInt).

Но, возможно, дело не в именах. Это о том, как вы проектируете параметры ввода / вывода вашего метода.

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

В C # я предпочитаю использовать параметр out. Используя out я отмечаю параметр как побочный параметр. Специально, что C # 6 будет поддерживать объявление встроенных переменных.

DoStuff(out var isStuffDone); // The out parameter name clearly states its purpose.
DoStuff(out var _); // I use _ for naming parameters that I am ignoring.
000
источник
0

Я бы выбрал имя в зависимости от основной роли метода. Тот факт, что метод возвращает логическое значение, не обязывает префикс «Is». Давайте немного Java-кода, который довольно широко использует префикс «Is». Возьмите взгляд на java.io.File.delete(). Это возвращается boolean, но это не называется isFileDeleted(). Причина в том, что основная роль метода заключается в удалении файла. Кто-то вызывает этот метод с намерением удалить файл.

Логическое значение существует только для того, чтобы сообщить результат работы. С другой стороны посмотрим java.util.Map.isEmpty(). Очевидно, вы вызываете такой метод, чтобы узнать, пуста ли коллекция. Вы не хотите ничего делать. Вы просто хотите узнать, каково текущее состояние.

Так что лично я бы точно придерживался DoStuff().

Это несколько очень важный вопрос для меня. Много раз, когда я поддерживаю унаследованный код, я сталкиваюсь с тем, что лично называю «ложным методом». Имя предлагает действие A, но тело выполняет действие B. Самый странный пример - метод получения, изменяющий данные в базе данных.

Яцек Прусия
источник