У меня есть метод, который должен вернуть объект, если он найден.
Если это не найдено, я должен:
- вернуть ноль
- бросить исключение
- Другой
У меня есть метод, который должен вернуть объект, если он найден.
Если это не найдено, я должен:
Ответы:
Если вы всегда ожидаете найти значение, выведите исключение, если оно отсутствует. Исключение будет означать, что возникла проблема.
Если значение может отсутствовать или присутствовать, и оба они действительны для логики приложения, вернуть ноль.
Более важно: что вы делаете в других местах в коде? Последовательность важна.
GetPersonById(25)
выбросит, если этот человек был удален, но GetPeopleByHairColor("red")
вернет пустой результат. Итак, я думаю, что параметры говорят об ожиданиях.
Только бросить исключение, если это действительно ошибка. Если ожидаемое поведение для объекта не существует, вернуть ноль.
В противном случае это вопрос предпочтения.
Как правило, если метод должен всегда возвращать объект, тогда используйте исключение. Если вы ожидаете случайного нулевого значения и хотите обработать его определенным образом, используйте нулевое значение.
Что бы вы ни делали, я настоятельно рекомендую против третьего варианта: возвращать строку с надписью "WTF".
Если null никогда не указывает на ошибку, просто верните null.
Если ноль всегда является ошибкой, тогда выдается исключение.
Если null иногда является исключением, тогда кодируйте две подпрограммы. Одна подпрограмма выдает исключение, а другая - логическую тестовую подпрограмму, которая возвращает объект в выходном параметре, а подпрограмма возвращает false, если объект не был найден.
Трудно неправильно использовать процедуру Try. Это действительно легко забыть проверить на ноль.
Так что, когда ноль является ошибкой, вы просто пишете
object o = FindObject();
Когда ноль не является ошибкой, вы можете написать что-то вроде
if (TryFindObject(out object o)
// Do something with o
else
// o was not found
find
и findOrFail
от Laravel Eloquent
TryFindObject
метода? Кортежи кажутся более ленивой парадигмой для программистов, которые не хотят тратить время на определение объекта, который инкапсулирует множество значений. То есть по сути все кортежи лежат в основе в любом случае.
Я просто хотел повторить перечисленные выше варианты, добавив несколько новых:
Или вы можете объединить эти параметры:
Предоставьте несколько перегруженных версий вашего геттера, чтобы вызывающий мог решить, каким путем идти. В большинстве случаев только первый имеет реализацию алгоритма поиска, а остальные просто оборачивают первый:
Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);
Даже если вы решите предоставить только одну реализацию, вы можете захотеть использовать соглашение об именах, подобное этому, для уточнения своего контракта, и это поможет вам, если вы когда-нибудь решите добавить другие реализации.
Вы не должны злоупотреблять этим, но это может быть полезно, особенно при написании вспомогательного класса, который вы будете использовать в сотнях различных приложений со многими различными соглашениями об обработке ошибок.
Expected<T> findObject(String)
где Expected<T>
есть функции orNull()
, orThrow()
, orSupplied(Supplier<T> supplier)
, orDefault(T default)
. Это абстрагирует получение данных от обработки ошибок
Используйте шаблон нулевого объекта или создайте исключение.
Person somePerson = personRepository.find("does-not-exist");
предположим, что этот метод возвращает нулевой объект для идентификатора does-not-exist
. Что тогда будет правильным поведением somePerson.getAge()
? Сейчас я еще не убежден, что шаблон нулевого объекта является правильным решением для поиска сущностей.
Преимущества броска исключения:
Для более подробного объяснения с примерами, см .: http://metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/
это зависит от того, продвигает ли ваш язык и код: LBYL (смотри, прежде чем прыгнуть) или EAFP (проще просить прощения, чем разрешения)
LBYL говорит, что вы должны проверить значения (так что возвращайте ноль)
EAFP говорит, чтобы просто попробовать операцию и посмотреть, если она потерпит неудачу (выбросить исключение)
хотя я согласен с вышесказанным .. исключения должны использоваться для исключительных ситуаций / ошибок, и возвращение нулевого значения лучше при использовании проверок.
EAFP против LBYL в Python:
http://mail.python.org/pipermail/python-list/2003-May/205182.html
( веб-архив )
Просто спросите себя: «Это исключительный случай, когда объект не найден»? Если ожидается, что это произойдет в ходе обычной программы, вам, вероятно, не следует выдавать исключение (поскольку это не исключительное поведение).
Краткая версия: используйте исключения для обработки исключительного поведения, а не для управления потоком управления в вашей программе.
-Алан.
Исключения связаны с проектированием по контракту.
Интерфейс объекта - это фактически контракт между двумя объектами, вызывающий должен выполнить контракт, иначе получатель может просто потерпеть неудачу с исключением. Есть два возможных контракта
1) все входные данные метода действительны, и в этом случае вы должны вернуть ноль, если объект не найден.
2) допустим только некоторый ввод, то есть тот, который приводит к найденному объекту. В этом случае вы ДОЛЖНЫ предложить второй метод, который позволяет вызывающей стороне определить, будет ли его ввод правильным. Например
is_present(key)
find(key) throws Exception
ЕСЛИ И ТОЛЬКО ЕСЛИ вы предоставляете оба метода 2-го контракта, вы можете выбросить исключение, если ничего не найдено!
Я предпочитаю просто возвращать нуль и полагаться на вызывающего, чтобы обработать его соответствующим образом. Исключение (из-за отсутствия лучшего слова) - если я абсолютно «уверен», этот метод вернет объект. В этом случае сбой является исключительным, должен и должен бросить.
Зависит от того, что значит, что объект не найден.
Если это нормальное положение вещей, верните ноль. Это просто то, что может происходить время от времени, и вызывающие абоненты должны это проверить.
Если это ошибка, то выдается исключение, вызывающие абоненты должны решить, что делать с условием ошибки отсутствующего объекта.
В конечном итоге любой из них сработает, хотя большинство людей обычно считают хорошей практикой использовать Исключения только тогда, когда что-то, ну, в общем, Исключительное произошло.
Вот еще пара предложений.
Если вы возвращаете коллекцию, избегайте возврата null, верните пустую коллекцию, что облегчает работу с перечислением без проверки на ноль.
Несколько API .NET используют шаблон параметра thrownOnError, который дает вызывающей стороне выбор, является ли это действительно исключительной ситуацией или нет, если объект не найден. Type.GetType является примером этого. Другим распространенным шаблоном с BCL является шаблон TryGet, в котором возвращается логическое значение, а значение передается через выходной параметр.
Вы можете также рассмотреть шаблон Null Object в некоторых обстоятельствах, которые могут быть по умолчанию или версией без поведения. Ключ заключается в том, чтобы избежать нулевых проверок по всей базе кода. Смотрите здесь для получения дополнительной информации http://geekswithblogs.net/dsellers/archive/2006/09/08/90656.aspx
В некоторые функции я добавляю параметр:
..., bool verify = true)
True означает бросок, false означает возвращение некоторого возвращаемого значения ошибки. Таким образом, тот, кто использует эту функцию, имеет обе опции. Значение по умолчанию должно быть истинным для тех, кто забывает об обработке ошибок.
Возврат null вместо выдачи исключения и четкое документирование возможности нулевого возвращаемого значения в документации API. Если вызывающий код не соблюдает API и проверяет нулевой регистр, он, скорее всего, в любом случае приведет к некоторому «исключению нулевого указателя» :)
В C ++ я могу представить 3 различных варианта настройки метода, который находит объект.
Вариант А
Object *findObject(Key &key);
Вернуть ноль, когда объект не может быть найден. Красиво и просто. Я бы пошел с этим. Альтернативные подходы ниже для людей, которые не ненавидят out-params.
Вариант Б
void findObject(Key &key, Object &found);
Передайте ссылку на переменную, которая будет получать объект. Метод выдает исключение, когда объект не может быть найден. Это соглашение, вероятно, будет более подходящим, если на самом деле не ожидается, что объект не будет найден - поэтому вы бросаете исключение, чтобы указать, что это неожиданный случай.
Вариант С
bool findObject(Key &key, Object &found);
Метод возвращает false, когда объект не может быть найден. Преимущество этого варианта перед А заключается в том, что вы можете проверить наличие ошибки за один четкий шаг:
if (!findObject(myKey, myObj)) { ...
ссылаясь только на случай, когда null не считается исключительным поведением, я определенно являюсь для метода try, это ясно, нет необходимости «читать книгу» или «смотреть, прежде чем прыгнуть», как было сказано здесь
так в основном:
bool TryFindObject(RequestParam request, out ResponseParam response)
а это значит, что код пользователя тоже будет понятен
...
if(TryFindObject(request, out response)
{
handleSuccess(response)
}
else
{
handleFailure()
}
...
Обычно он должен возвращать ноль. Код, вызывающий метод, должен решить, выдавать ли исключение или пытаться что-то еще.
Или верните опцию
Опция - это, по сути, контейнерный класс, который заставляет клиента обрабатывать стенды. У Scala есть такая концепция, посмотрите, это API.
Затем у вас есть методы, такие как T getOrElse (T valueIfNull) для этого объекта, которые либо возвращают найденный объект, либо альтернативу, указанную клиентом.
К сожалению, JDK несовместим, если вы пытаетесь получить доступ к несуществующему ключу в комплекте ресурсов, вы получаете исключение not found, а когда вы запрашиваете значение из карты, вы получаете нулевое значение, если оно не существует. Поэтому я бы изменил ответ победителя следующим образом: если найденное значение может быть нулевым, то возбудить исключение, если оно не найдено, в противном случае вернуть нулевое значение. Так что следуйте правилу с одним исключением, если вам нужно знать, почему значение не найдено, всегда вызывайте исключение, или ..
До тех пор, пока он должен возвращать ссылку на объект, возвращать NULL должно быть хорошо.
Однако, если он возвращает всю кровавую вещь (как в C ++, если вы делаете: «return blah;» вместо «return & blah;» (или «blah» - указатель)), то вы не можете вернуть NULL, потому что это не типа «объект». В этом случае я бы выбрал проблему или выдал пустой объект, для которого не установлен флаг успеха.
Не думайте, что кто-то упомянул о накладных расходах при обработке исключений - для загрузки и обработки исключения требуются дополнительные ресурсы, поэтому, если только это не является действительным событием уничтожения приложения или остановки процесса (продвижение вперед принесет больше вреда, чем пользы), я бы предпочел передать обратно Значение вызывающей среды можно интерпретировать так, как она считает нужным.
Я согласен с тем, что кажется консенсусом здесь (верните ноль, если «не найдено» - нормальный возможный результат, или выведите исключение, если семантика ситуации требует, чтобы объект всегда находился).
Однако есть и третья возможность, которая может иметь смысл в зависимости от вашей конкретной ситуации. Ваш метод может вернуть объект по умолчанию некоторого вида в состоянии «не найден», что позволяет вызывающему коду быть уверенным, что он всегда получит действительный объект без необходимости проверки на нуль или перехвата исключений.
Исключения должны быть исключительными . Вернуть ноль, если допустимо вернуть ноль .
Если метод возвращает коллекцию, верните пустую коллекцию (как сказано выше). Но, пожалуйста, не Collections.EMPTY_LIST или что-то подобное! (в случае Java)
Если метод получает один объект, у вас есть несколько вариантов.
Будьте осторожны, если вы решили вернуть ноль. Если вы не единственный программист в проекте, вы получите NullPointerExceptions (на Java или на других языках) во время выполнения! Так что не возвращайте нули, которые не проверяются во время компиляции.
null
. См. Голосование с наибольшим количеством голосов для получения дополнительной информации.
Если вы используете библиотеку или другой класс, который выдает исключение, вы должны выбросить его заново . Вот пример. Example2.java похож на библиотеку, а Example.java использует его объект. Main.java является примером для обработки этого исключения. Вы должны показать осмысленное сообщение и (при необходимости) трассировку стека пользователю на вызывающей стороне.
Main.java
public class Main {
public static void main(String[] args) {
Example example = new Example();
try {
Example2 obj = example.doExample();
if(obj == null){
System.out.println("Hey object is null!");
}
} catch (Exception e) {
System.out.println("Congratulations, you caught the exception!");
System.out.println("Here is stack trace:");
e.printStackTrace();
}
}
}
Example.java
/**
* Example.java
* @author Seval
* @date 10/22/2014
*/
public class Example {
/**
* Returns Example2 object
* If there is no Example2 object, throws exception
*
* @return obj Example2
* @throws Exception
*/
public Example2 doExample() throws Exception {
try {
// Get the object
Example2 obj = new Example2();
return obj;
} catch (Exception e) {
// Log the exception and rethrow
// Log.logException(e);
throw e;
}
}
}
Example2.java
/**
* Example2.java
* @author Seval
*
*/
public class Example2 {
/**
* Constructor of Example2
* @throws Exception
*/
public Example2() throws Exception{
throw new Exception("Please set the \"obj\"");
}
}
Это действительно зависит от того, ожидаете ли вы найти объект или нет. Если вы следуете школе мысли, что исключения должны использоваться для обозначения чего-то, ну, а, конечно, произошло исключение:
В противном случае верните ноль.