Положительные стороны: мощный; позволяет Вам:
Отрицательные стороны: мощный; позволяет Вам:
Шаблоны значительно увеличивают жизнеспособное пространство дизайна, которое является не обязательно плохой вещью, но оно действительно делает их что намного тяжелее использовать хорошо. Шаблону кода нужен maintainters, кто понимает не только функции языка, но и последствия дизайна функций языка; в сущности это означает, что много групп разработчиков избегают всех кроме самых простых и наиболее узаконенных приложений шаблонов C++.
В целом, шаблоны делают язык намного более сложным (и трудный реализовать правильно!). Шаблоны не были намеренно разработаны для Тьюринга, но они так или иначе - таким образом, даже при том, что они могут сделать примерно, что-либо, с помощью них может оказаться большей проблемой, чем это стоит.
Обычно лучше всего возвращать значение null, если вы хотите указать, что данные недоступны.
Пустой объект подразумевает, что данные были возвращены, тогда как возврат null явно указывает, что ничего не было возвращено.
Кроме того, возврат null приведет к нулевому исключению, если вы попытаетесь получить доступ к членам в объекте, что может быть полезно для выделения ошибочного кода - попытки получить доступ к члену ничего не имеют смысла. Доступ к элементам пустого объекта не приведет к ошибке, что означает, что ошибки могут остаться незамеченными.
Для типов коллекций я бы возвращал пустую коллекцию, для всех других типов я предпочитаю использовать шаблоны NullObject для возврата объекта, реализующего тот же интерфейс, что и у возвращаемого типа. подробности о шаблоне см. текст ссылки
При использовании шаблона NullObject это будет: -
public UserEntity GetUserById(Guid userId)
{ // Представьте здесь какой-то код для доступа к базе данных .....
//Check if data was returned and return a null if none found
if (!DataExists)
return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();
else
return existingUserEntity;
}
class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...}
Обычно я возвращаю значение null. Он обеспечивает быстрый и простой механизм обнаружения каких-либо проблем без создания исключений и повсеместного использования множества попыток / отлова.
Я лично возвращаю экземпляр объекта по умолчанию. Причина в том, что я ожидаю, что метод вернет ноль ко многим или ноль к единице (в зависимости от цели метода). Единственная причина, по которой при использовании этого подхода это может быть состояние ошибки любого рода, заключается в том, что метод не возвращал никаких объектов и всегда ожидался (с точки зрения возврата «один ко многим» или единственного числа).
Как к предположению, что это вопрос из области бизнеса - я просто не вижу его с этой стороны уравнения. Нормализация возвращаемых типов - это правильный вопрос архитектуры приложения. По крайней мере, он подлежит стандартизации в практике кодирования. Я сомневаюсь, что есть бизнес-пользователь, который скажет «в сценарии X просто дайте ему ноль».
Лично я использую NULL. Он дает понять, что данных для возврата нет. Но бывают случаи, когда Нулевой объект может оказаться полезным.
Если ваш возвращаемый тип - массив, тогда верните пустой массив, иначе верните ноль.
Вы должны генерировать исключение (только), если конкретный контракт нарушен.
В вашем конкретном примере, запрашивающем UserEntity на основе известного идентификатора, это будет зависеть от того, ожидаются ли отсутствующие (удаленные) пользователи. Если да, то верните null
, но если это не ожидаемый случай, то вызовите исключение.
Обратите внимание, что если бы функция была вызвана UserEntity GetUserByName (имя строки)
, она, вероятно, не выдала бы, а вернула бы null. В обоих случаях возврат пустого UserEntity будет бесполезным.
Для строк, массивов и коллекций ситуация обычно иная. Я помню некую директиву от MS о том, что методы должны принимать null
как «пустой» список, но возвращать коллекции нулевой длины, а не null
. То же и со струнами. Обратите внимание, что вы можете объявлять пустые массивы: int [] arr = new int [0];
В нашем бизнесе У нас есть 2 основных метода Get:
Для простоты в контексте, если вы сомневаетесь, что они будут такими:
// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}
// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}
Первый метод используется при получении определенных сущностей, второй метод используется специально при добавлении или редактировании сущностей на веб-страницы.
Это позволяет нам получить лучшее из обоих миров в контексте, в котором они используются.
Лично я бы вернул значение null, потому что именно так будет действовать уровень DAL / Repository.
Если он не существует, не возвращайте ничего, что могло бы быть истолковано как успешное извлечение объекта, null
здесь прекрасно работает.
Самое главное, чтобы ваш DAL был согласован. / Repos Layer, так что вы не запутаетесь, как его использовать.
Это зависит от того, что больше всего подходит для вашего случая.
Имеет ли смысл возвращать значение null, например, «такого пользователя не существует»?
Или имеет смысл создавать пользователя по умолчанию? Это имеет наибольший смысл, когда вы можете с уверенностью предположить, что если пользователь НЕ существует, вызывающий код предполагает, что он существует, когда они его запрашивают.
Или имеет смысл генерировать исключение (а-ля "FileNotFound" "), если вызывающий код запрашивает пользователя с недопустимым идентификатором?
Однако с точки зрения разделения проблем / SRP первые два более правильны. И технически первый является наиболее правильным (но только на волосок) - GetUserById должен отвечать только за одно - получение пользователя. Собственная обработка " Разделение на другую проверку - bool DoesUserExist (id)
будет уместным, если вы выберете исключение.
На основе подробных комментариев ниже : если это вопрос проектирования уровня API , этот метод может быть аналогичен OpenFile или ReadEntireFile. Мы «открываем» пользователя из некоторого репозитория и гидратируем объект из полученных данных. В этом случае подходит исключение . Возможно, это не так, но может быть.
Все подходы приемлемы - это просто зависит от более широкого контекста API / приложения.
Разделение на другую проверку - bool DoesUserExist (id)
будет уместным, если вы выберете исключение.
На основе подробных комментариев ниже : если это вопрос проектирования уровня API , этот метод может быть аналогичен OpenFile или ReadEntireFile. Мы «открываем» пользователя из некоторого репозитория и гидратируем объект из полученных данных. В этом случае может быть подходящим исключение . Может и не быть, но может быть.
Все подходы приемлемы - это просто зависит от более широкого контекста API / приложения.
Это будет зависеть от контекста, но я обычно возвращаю null, если ищу один конкретный объект (как в вашем примере), и возвращаю пустую коллекцию, если я ищу набор объектов, но их нет.
Если вы допустили ошибку в своем коде и возврат null приводит к исключениям с нулевым указателем, то чем раньше вы это поймете, тем лучше. Если вы вернете пустой объект, его первоначальное использование может сработать, но позже вы можете получить ошибки.
Лучшее в этом случае возвращает "null" в случае, если такого пользователя нет. Также сделайте свой метод статическим.
Изменить:
Обычно такие методы являются членами некоторого класса «Пользователь» и не имеют доступа к членам его экземпляра. В этом случае метод должен быть статическим, иначе вы должны создать экземпляр «User», а затем вызвать метод GetUserById, который вернет другой экземпляр «User». Согласитесь, это сбивает с толку. Но если метод GetUserById является членом какого-либо класса "DatabaseFactory" - нет проблем оставить его в качестве члена экземпляра.
Это бизнес-вопрос, зависящий от того, является ли существование пользователя с определенным идентификатором Guid ожидаемым нормальным вариантом использования этой функции, или это аномалия, которая помешает приложению успешное завершение любой функции, которую этот метод предоставляет объекту пользователя ...
Если это «исключение», то есть отсутствие пользователя с этим идентификатором помешает приложению успешно завершить любую выполняемую им функцию (скажем, мы создаем счет-фактуру для клиента, которому мы отправили продукт ...), тогда в этой ситуации должно возникнуть исключение ArgumentException (или другое настраиваемое исключение).
Если с пропавшим пользователем все в порядке (один из возможных нормальных результатов вызова этой функции), вернуть ноль ....
ИЗМЕНИТЬ: (чтобы ответить на комментарий Адама в другом ответе)
Если приложение содержит несколько бизнес-процессов, один или несколько из которых требуют участия пользователя для успешного завершения, и один или несколько из которых могут успешно завершиться без пользователя, то исключение должно быть выброшено выше по стеку вызовов, ближе к тому месту, где бизнес-процессы, требующие пользователя, вызывают этот поток выполнения. Методы между этим методом и этой точкой (где генерируется исключение) должны просто сообщать, что пользователя не существует (null, логическое значение, что угодно - это деталь реализации).
Но если все процессы в приложении требуют пользователя, я все равно выбрасываю исключение в этом методе ...
для одного или нескольких из которых требуется Пользователь для успешного завершения, и один или несколько из которых могут успешно завершиться без пользователя, тогда исключение должно быть выброшено дальше по стеку вызовов, ближе к тому месту, где находятся бизнес-процессы, требующие пользователя вызывая этот поток выполнения. Методы между этим методом и этой точкой (где генерируется исключение) должны просто сообщать, что пользователя не существует (null, логическое значение, что угодно - это деталь реализации).Но если все процессы в приложении требуют пользователя, я все равно выбрасываю исключение в этом методе ...
один или несколько из которых требуют пользователя для успешного завершения, и один или несколько из которых могут успешно завершиться без пользователя, тогда исключение должно быть выброшено дальше в стек вызовов, ближе к тому месту, где находятся бизнес-процессы, требующие пользователя вызывая этот поток выполнения. Методы между этим методом и этой точкой (в которой создается исключение) должны просто сообщать, что пользователя не существует (null, логическое значение, что угодно - это деталь реализации).Но если все процессы в приложении требуют пользователя, я все равно выбрасываю исключение в этом методе ...
ближе к тому месту, где бизнес-процессы, требующие пользователя, вызывают этот поток выполнения. Методы между этим методом и этой точкой (в которой создается исключение) должны просто сообщать, что пользователя не существует (null, логическое значение, что угодно - это деталь реализации).Но если все процессы в приложении требуют пользователя, я все равно выбрасываю исключение в этом методе ...
ближе к тому месту, где бизнес-процессы, требующие от пользователя, вызывают этот поток выполнения. Методы между этим методом и этой точкой (где генерируется исключение) должны просто сообщать, что пользователя не существует (null, логическое значение, что угодно - это деталь реализации).Но если все процессы в приложении требуют пользователя, я все равно выбрасываю исключение в этом методе ...
Простите мой псевдо-php / код.
Я думаю, это действительно зависит от предполагаемого использования результата.
Если вы намереваетесь отредактировать / изменить возвращаемое значение и сохранить его, то верните пустой объект. Таким образом, вы можете использовать ту же функцию для заполнения данных нового или существующего объекта.
Допустим, у меня есть функция, которая принимает первичный ключ и массив данных, заполняет строку данными, а затем сохраняет полученную запись в базу данных. Поскольку я собираюсь заполнить объект своими данными в любом случае, получение пустого объекта из получателя может быть огромным преимуществом. Таким образом, я могу выполнять идентичные операции в любом случае. Вы используете результат функции получения, несмотря ни на что.
Пример:
function saveTheRow($prim_key, $data) {
$row = getRowByPrimKey($prim_key);
// Populate the data here
$row->save();
}
Здесь мы видим, что одна и та же последовательность операций обрабатывает все записи этого типа.
Однако, если конечная цель возвращаемого значения - прочитать и что-то сделать с данными, я бы вернул null. Таким образом, я могу очень быстро определить, не были ли возвращены данные, и отобразить соответствующее сообщение пользователю.
Обычно я перехватываю исключения в своей функции, которая извлекает данные (чтобы я мог регистрировать сообщения об ошибках и т. Д.), А затем возвращаю null прямо из перехвата. Обычно для конечного пользователя не имеет значения, в чем проблема, поэтому я считаю, что лучше всего инкапсулировать мою регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую базу кода в любой крупной компании, это особенно полезно, потому что вы можете принудительно регистрировать / обрабатывать ошибки даже у самого ленивого программиста.
Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока все работает хорошо.
Таким образом, я могу очень быстро определить, не были ли возвращены данные, и отобразить соответствующее сообщение пользователю.Обычно я перехватываю исключения в своей функции, которая извлекает данные (чтобы я мог регистрировать сообщения об ошибках и т. Д.), А затем возвращаю null прямо из перехвата. Обычно для конечного пользователя не имеет значения, в чем проблема, поэтому я считаю, что лучше всего инкапсулировать регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую базу кода в любой крупной компании, это особенно полезно, потому что вы можете принудительно вести журнал / обработку ошибок даже у самого ленивого программиста.
Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока что это работает хорошо.
Таким образом, я могу очень быстро определить, не были ли возвращены данные, и отобразить соответствующее сообщение пользователю.Обычно я перехватываю исключения в своей функции, которая извлекает данные (чтобы я мог регистрировать сообщения об ошибках и т. Д.), А затем возвращаю null прямо из перехвата. Обычно для конечного пользователя не имеет значения, в чем проблема, поэтому я считаю, что лучше всего инкапсулировать регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую базу кода в любой крупной компании, это особенно полезно, потому что вы можете принудительно вести журнал / обработку ошибок даже у самого ленивого программиста.
Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока все работает хорошо.
ll перехватывают исключения в моей функции, которая извлекает данные (чтобы я мог регистрировать сообщения об ошибках и т. д.), а затем возвращать null прямо из перехвата. Обычно для конечного пользователя не имеет значения, в чем проблема, поэтому я считаю, что лучше всего инкапсулировать регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую кодовую базу в любой крупной компании, это особенно полезно, потому что вы можете заставить правильную регистрацию / обработку ошибок даже у самого ленивого программиста.Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока все работает хорошо.
ll перехватывает исключения в моей функции, которая извлекает данные (чтобы я мог регистрировать сообщения об ошибках и т. д.), а затем возвращать null прямо из перехвата. Обычно для конечного пользователя не имеет значения, в чем проблема, поэтому я считаю, что лучше всего инкапсулировать регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую базу кода в любой крупной компании, это особенно полезно, потому что вы можете принудительно вести журнал / обработку ошибок даже у самого ленивого программиста.Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока что это работает хорошо.
поэтому я считаю, что лучше всего инкапсулировать мою регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую кодовую базу в любой крупной компании, это особенно полезно, потому что вы можете заставить правильную регистрацию / обработку ошибок даже у самого ленивого программиста.Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока все работает хорошо.
поэтому я считаю, что лучше всего инкапсулировать мою регистрацию / обработку ошибок непосредственно в функции, которая получает данные. Если вы поддерживаете общую кодовую базу в любой крупной компании, это особенно полезно, потому что вы можете заставить правильную регистрацию / обработку ошибок даже у самого ленивого программиста.Пример:
function displayData($row_id) {
// Logging of the error would happen in this function
$row = getRow($row_id);
if($row === null) {
// Handle the error here
}
// Do stuff here with data
}
function getRow($row_id) {
$row = null;
try{
if(!$db->connected()) {
throw excpetion("Couldn't Connect");
}
$result = $db->query($some_query_using_row_id);
if(count($result) == 0 ) {
throw new exception("Couldn't find a record!");
}
$row = $db->nextRow();
} catch (db_exception) {
//Log db conn error, alert admin, etc...
return null; // This way I know that null means an error occurred
}
return $row;
}
Это мое общее правило. Пока что это работает хорошо.
If the case of the user not being found comes up often enough, and you want to deal with that in various ways depending on circumstance (sometimes throwing an exception, sometimes substituting an empty user) you could also use something close to F#'s Option
or Haskell's Maybe
type, which explicitly seperates the 'no value' case from 'found something!'. The database access code could look like this:
public Option<UserEntity> GetUserById(Guid userId)
{
//Imagine some code here to access database.....
//Check if data was returned and return a null if none found
if (!DataExists)
return Option<UserEntity>.Nothing;
else
return Option.Just(existingUserEntity);
}
And be used like this:
Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
// deal with it
} else {
UserEntity value = result.GetValue();
}
Unfortunately, everybody seems to roll a type like this of their own.
Больше мяса для измельчения: допустим, мой DAL возвращает NULL для GetPersonByID, как некоторые советуют. Что должен делать мой (довольно тонкий) BLL, если он получает NULL? Передать это значение NULL и позволить конечному потребителю беспокоиться об этом (в данном случае - странице ASP.Net)? Как насчет того, чтобы BLL генерировал исключение?
BLL может использоваться ASP.Net и Win App или другой библиотекой классов - я думаю, что несправедливо ожидать, что конечный потребитель внутренне «знает», что метод GetPersonByID возвращает ноль (если, я полагаю, не используются нулевые типы).
Я считаю, что мой DAL возвращает NULL, если ничего не найдено. ДЛЯ НЕКОТОРЫХ ОБЪЕКТОВ это нормально - это может быть список вещей 0: многие, так что ничего не иметь (например, список любимых книг). В этом случае мой BLL возвращает пустой список. Для большинства отдельных объектов (например, пользователя, учетной записи, счета-фактуры), если у меня их нет, это определенно проблема и вызовет дорогостоящее исключение. Однако, поскольку получение пользователя по уникальному идентификатору, который ранее был предоставлен приложением, всегда должно возвращать пользователя, исключение является «правильным» исключением, поскольку оно является исключительным. Конечный потребитель BLL (ASP.Net, f'rinstance) всегда ожидает, что все будет в порядке, поэтому будет использоваться обработчик необработанных исключений вместо того, чтобы заключать каждый вызов GetPersonByID в блок try-catch.
Если в моем подходе есть очевидная проблема, пожалуйста, дайте мне знать, так как я всегда стремлюсь учиться. Как было сказано на других плакатах, исключения - вещь дорогостоящая, и подход «сначала проверка» хорош, но исключения должны быть просто исключительными.
Интересный вопрос, и я думаю, что на него нет "правильного" ответа, поскольку он всегда зависит от ответственности вашего кода. Знает ли ваш метод, являются ли обнаруженные данные проблемой или нет? В большинстве случаев ответ - «нет», и именно поэтому возвращение null и предоставление вызывающей возможности обработки этой ситуации является идеальным.
Может быть, хороший способ отличить методы бросания от методов, возвращающих null, - это найти соглашение в вашей команде: Методы которые говорят, что «получили» что-то, должны вызывать исключение, если нечего получить. Методы, которые могут возвращать значение null, можно было бы назвать по-другому, например, «Найти ...».
Я французский студент, изучающий информационные технологии, так что извините за мой плохой английский. В наших классах нам говорят, что такой метод никогда не должен возвращать ни null, ни пустой объект. Пользователь этого метода должен сначала проверить, существует ли объект, который он ищет, прежде чем пытаться его получить.
Используя Java, нас просят добавить assert exists (object): «Вы не должны попытаться получить доступ к несуществующему объекту »;
в начале любого метода, который мог бы возвращать значение null, чтобы выразить« предварительное условие »(я не знаю, что это за слово по-английски).
IMO это действительно непросто, но я использую это в ожидании чего-то лучшего.
Я предпочитаю null
, поскольку он совместим с оператором объединения с нулем ( ??
).
Я бы сказал, что возвращает null вместо пустого объекта.
Но конкретный экземпляр, который вы здесь упомянули, вы ищете пользователя по идентификатору пользователя, который является сортировкой ключа этого пользователя, в этом случае я бы, вероятно, захотел чтобы вызвать исключение, если экземпляр экземпляра пользователя не найденный.
Я обычно следую этому правилу:
Мы используем CSLA.NET, и он принимает точку зрения, что неудачная выборка данных должна возвращать "пустой" объект. На самом деле это довольно раздражает, поскольку требует соглашения о проверке того, является ли obj.IsNew
, а не obj == null
.
Как упоминалось ранее, null return значения приведут к сбою кода сразу, уменьшая вероятность проблем со скрытностью, вызванных пустыми объектами.
Лично я считаю null
более элегантным.
Это очень распространенный случай, и я ' m удивлен, что люди здесь кажутся удивленными: в любом веб-приложении данные часто выбираются с использованием параметра строки запроса, который, очевидно, может быть изменен, поэтому разработчик должен обрабатывать случаи «не найден».
Вы можете справиться с этим с помощью:
if (User.Exists(id)) { this.User = User.Fetch(id); } else { Response.Redirect("~/notfound.aspx"); }
... но это дополнительный вызов базы данных каждый раз, что может быть проблемой на страницах с высоким трафиком. Принимая во внимание:
this.User = User.Fetch(id); if (this.User == null) { Response.Redirect("~/notfound.aspx"); }
... требуется только один звонок.
Чтобы указать другие сказал более содержательно ...
Исключения для исключительных обстоятельств
Если этот метод является чистым уровнем доступа к данным, я бы сказал, что, учитывая некоторый параметр, который включается в оператор выбора, можно ожидать, что я могу не найти никаких строк, из которых можно построить объект, и поэтому возврат null будет приемлемым, поскольку это логика доступа к данным.
С другой стороны, если я ожидал, что мой параметр будет отражать первичный ключ, и я должен вернуть только одну строку назад, если бы мне вернули более одного, я бы выбросил исключение. 0 - это нормально для возврата null, 2 - нет.
Теперь, если бы у меня был какой-то код входа, который проверялся по поставщику LDAP, а затем проверялся по БД, чтобы получить более подробную информацию, и я ожидал, что они должны всегда синхронизироваться, я может тогда исключить исключение. Как говорили другие, это бизнес-правила.
Теперь я скажу, что это общее правило. Бывают моменты, когда вы можете захотеть сломать это. Однако мой опыт и эксперименты с C # (много такого) и Java (немного) научили меня, что обработка исключений на намного дороже с точки зрения производительности, чем обработка предсказуемых проблем с помощью условной логики. . Я говорю о мелодии на 2 или 3 порядка дороже в некоторых случаях. Итак, если это возможно, ваш код может зациклиться,
Если возвращенный объект является чем-то, что можно повторить , Я бы вернул пустой объект, чтобы мне не пришлось сначала проверять значение null.
Пример:
bool IsAdministrator(User user)
{
var groupsOfUser = GetGroupsOfUser(user);
// This foreach would cause a run time exception if groupsOfUser is null.
foreach (var groupOfUser in groupsOfUser)
{
if (groupOfUser.Name == "Administrators")
{
return true;
}
}
return false;
}
Еще один подход включает передачу объекта обратного вызова или делегата, который будет работать со значением. Если значение не найдено, обратный вызов не вызывается.
public void GetUserById(Guid id, UserCallback callback)
{
// Lookup user
if (userFound)
callback(userEntity); // or callback.Call(userEntity);
}
Это хорошо работает, когда вы хотите избежать нулевых проверок по всему коду, и когда не нахождение значения не является ошибкой. Вы также можете предоставить обратный вызов, когда объекты не найдены, если вам нужна особая обработка.
public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
// Lookup user
if (userFound)
callback(userEntity); // or callback.Call(userEntity);
else
notFound(); // or notFound.Call();
}
Тот же подход с использованием одного объекта может выглядеть так:
public void GetUserById(Guid id, UserCallback callback)
{
// Lookup user
if (userFound)
callback.Found(userEntity);
else
callback.NotFound();
}
С точки зрения дизайна мне очень нравится этот подход, но он имеет недостаток в том, что сайт звонков становится громоздким на языках, которые не поддерживают функции первого класса.