Какой контракт (Дизайн по контракту) лучше? - PullRequest
8 голосов
/ 05 января 2009

Предположим, у меня есть метод

public Patient(int id)
{
    ----
}

, который возвращает объект Patient с указанным идентификатором. Я мог бы определить контракт двумя способами

  1. Метод вернул бы ноль, если пациент не существует
  2. Метод выдаст исключение, если пациента не существует. В этом случае я бы также определил метод запроса, который возвращает true, если пациент существует в базе данных, или false в противном случае ...

Какой контракт я должен использовать? Любые другие предложения?

Обновление: пожалуйста, прокомментируйте этот случай тоже ... Если это не идентификатор, назначенный базе данных, и это что-то, что пользователь вводит в пользовательском интерфейсе, например, SSN, то какой из них лучше ..

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

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

Комментарий Роба Уэллса о создании исключения, потому что его плохой идентификатор: я не думаю, что опечатка в имени пациента является исключительным обстоятельством "ИМХО

Ответы [ 9 ]

15 голосов
/ 05 января 2009

Имейте в виду, что переход "по проводам" на другой уровень (будь то база данных или сервер приложений) является одним из самых дорогих действий, которые вы можете сделать - обычно сетевой вызов занимает на несколько порядков больше, чем вызовы памяти.

Поэтому стоит структурировать свой API, чтобы избежать избыточных вызовов.

Подумайте, если ваш API похож на это:

// Check to see if a given patient exists
public bool PatientExists(int id);

// Load the specified patient; throws exception if not found
public Patient GetPatient(int id);

Тогда вы, вероятно, дважды попадете в базу данных или будете полагаться на хорошее кэширование, чтобы избежать этого.

Другое соображение заключается в следующем: в некоторых местах ваш код может иметь идентификатор «известный-хороший», в других - нет. Для каждого местоположения требуется отдельная политика относительно того, следует ли создавать исключение.

Вот шаблон, который я использовал для хорошего эффекта в прошлом - есть два метода:

// Load the specified patient; throws exception if not found
public Patient GetExistingPatient(int id);

// Search for the specified patient; returns null if not found
public Patient FindPatient(int id);

Понятно, что GetExistingPatient () можно построить, вызвав FindPatient ().

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

4 голосов
/ 05 января 2009

Вы, вероятно, должны выбросить исключение. Если у вас есть id, который не указывает на действительного пациента, откуда он взялся? Что-то очень плохое, вероятно, произошло. Это исключительное обстоятельство.

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

Функция поиска должна иметь контракт, отличный от функции поиска.

4 голосов
/ 05 января 2009

Другим вариантом будет шаблон Null Object .

2 голосов
/ 05 января 2009

Это зависит от:

Если вы считаете, что нормальная работа приведет к тому, что номер патиона не соответствует файлу в БД, тогда должна быть возвращена пустая (NULL) запись.

Но если вы ожидаете, что данный идентификатор всегда будет попадать в запись, тогда, когда он не найден (что должно быть редко), используйте исключение.

Другие вещи, такие как ошибка соединения с БД, должны создавать исключение.
Как и следовало ожидать, в нормальных ситуациях запрос к БД всегда будет работать (хотя он может возвращать 0 записей или нет).

P.S. Я бы не вернул указатель. (Кому принадлежит указатель ??)
Я бы вернул объект, который может иметь или не иметь запись. Но это вы можете общаться для существования записи внутри. Потенциально умный указатель или что-то немного умнее, чем умный указатель, который понимает текст.

2 голосов
/ 05 января 2009

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

Я предпочитаю использовать исключения, чтобы помочь серьезной деградации, когда есть проблема с самой системой.

В данном случае это, вероятно, mosdt:

  1. опечатка в ID пациента, если она была введена в форму поиска,
  2. ошибка ввода данных или
  3. проблема рабочего процесса в том, что запись о пациенте еще не была введена.

Следовательно, возвращает ноль, а не исключение.

Если при обращении к базе данных возникла проблема, я бы вызвал исключение метода.

Редактировать: Только что увидел, что идентификатор пациента в подписи был целым числом, спасибо Стивену Лоу, поэтому я исправил свой список причин.

Мой основной пункт о том, как определить, когда использовать исключения (для системных ошибок), в отличие от других методов возврата ошибки (для простых опечаток при вводе данных), все еще остается в силе. ИМХО.

НТН

ура

Rob

1 голос
/ 05 января 2009

принимая ваше описание по номиналу, вам, вероятно, понадобятся оба:

  • неверные идентификаторы - это ошибки / исключения, как указал Адам, но
  • если вам в других местах указаны идентификаторы, которые могли бы исчезнуть, вам понадобится метод запроса, чтобы проверить их
1 голос
/ 05 января 2009

В такой простой ситуации, как эта 1. кажется более чем достаточно. Вы можете захотеть реализовать что-то вроде метода обратного вызова, который клиент вызывает, чтобы узнать, почему он вернул null Просто предложение.

0 голосов
/ 05 января 2009

Брось исключение.

Если вы вернете ноль, введите код:

Console.WriteLine(Patient(id).Name);

завершится с ошибкой NullReferenceException, если идентификатор не существует, что не так полезно, как скажем, PatientNotFoundException (id). В этом примере все еще относительно легко отследить, но рассмотрим:

somePatient = Patient(id)

// much later, in a different function:

Console.WriteLine(somePatient);

О добавлении функции, которая проверяет, существует ли пациент: обратите внимание, что это не предотвратит исключение PatientNotFoundExceptions полностью. Например:

if (PatientExists(id))
    Console.WriteLine(Patient(id).Name);

- другой поток или другой процесс может удалить пациента между вызовами PatientExists и Patient. Кроме того, это будет означать два запроса к базе данных вместо одного. Как правило, лучше просто попробовать позвонить и обработать исключение.

Обратите внимание, что ситуация отличается для запросов, которые возвращают несколько значений, например, в виде списка; здесь уместно возвращать пустой список, если совпадений нет.

0 голосов
/ 05 января 2009

Если я правильно прочитал ... Когда вы звоните пациенту (100), он возвращает ссылку на объект для пациента с идентификатором 100. Если ни один пациент с идентификатором 100 не существует, я думаю, что он должен вернуть ноль. Исключения превышают IMO, и этот случай не требует этого. Функция просто вернула ноль. Это не создало какой-либо ошибочной ситуации, которая может привести к сбою вашего приложения (если, конечно, вы не обработали этот нуль и передали его какой-то другой части вашего приложения).

Я определенно хотел бы, чтобы эта функция возвращала 'null', особенно если бы это было частью какого-то поиска, когда пользователь будет искать пациента с определенным идентификатором, и если ссылка на объект в итоге окажется нулевой, он просто заявит пациент с таким идентификатором не существует.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...