Какой контракт (дизайн по контракту) лучше?
-
03-07-2019 - |
Вопрос
Предположим, у меня есть метод
public Patient(int id)
{
----
}
это возвращает объект Patient с заданным идентификатором..Я мог бы определить контракт двумя способами
- Метод вернет значение null, если пациент не существует
- Метод выдаст исключение, если пациент не существует.В этом случае я бы также определил метод запроса, который возвращает true, если Пациент существует в базе данных, или false в противном случае...
Какой контракт я должен использовать?Есть еще какие-нибудь предложения?
Обновить:Пожалуйста, прокомментируйте и этот случай...Если это не идентификатор, присвоенный базе данных, и это то, что пользователь вводит в пользовательском интерфейсе..как SSN ..тогда какой из них лучше..
Комментарий о нулевом шаблоне от Стива, который я считаю допустимым:вероятно, здесь не очень хорошая идея, так как было бы действительно полезно сразу узнать, когда идентификатор не существует.
И я также думаю, что нулевой шаблон здесь был бы несколько тяжеловесным
Комментарий Роба Уэллса о выбрасывании исключения из-за его неверного идентификатора:я не думаю, что опечатка в имени пациента является исключительным обстоятельством "ИМХО
Решение
Имейте в виду, что "по проводу" на другой уровень (будь то база данных или сервер приложений) является одним из самых дорогих действий, которые вы можете сделать - обычно сетевой вызов занимает на несколько порядков больше времени, чем вызовы в памяти.
Поэтому стоит структурировать свой 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);
Тогда вы, вероятно, дважды попадете в базу данных или будете полагаться на хорошее кэширование, чтобы избежать этого.
Еще одно соображение заключается в следующем: в некоторых местах ваш код может иметь " известное-хорошее " 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 ().
Это позволяет вашему вызывающему коду получить соответствующее поведение, генерировать исключение, если что-то пошло не так, и избегать обработки исключений в тех случаях, когда это не нужно.
Другие советы
Другой вариант - Шаблон пустых объектов .
Вы, вероятно, должны выбросить исключение. Если у вас есть id
, который не указывает на действительного пациента, откуда он взялся? Что-то очень плохое, вероятно, произошло. Это исключительное обстоятельство.
РЕДАКТИРОВАТЬ . Если вы делаете что-то кроме поиска, основанного на целочисленных значениях, например, поиск по тексту, тогда возвращение null
подойдет. Тем более, что в этом случае вы возвращаете набор результатов, который может быть более одного (более одного пациента с тем же именем, той же датой рождения или какими бы ни были ваши критерии). Р>
Функция поиска должна иметь контракт, отличный от функции поиска.
Это зависит от
Если вы считаете, что обычная операция приведет к тому, что номер патиона не соответствует файлу в БД, тогда должна быть возвращена пустая (NULL) запись.
Но если вы ожидаете, что данный идентификатор всегда будет попадать в запись, тогда, когда он не найден (что должно быть редко), используйте исключение.
Другие вещи, такие как ошибка соединения с БД, должны генерировать исключение.
Как и следовало ожидать, в нормальных ситуациях запрос к БД всегда будет работать (хотя он может возвращать 0 записей или нет).
P.S. Я бы не вернул указатель. (Кому принадлежит указатель ??)
Я бы вернул объект, который может иметь или не иметь запись. Но это вы можете общаться для существования записи внутри. Потенциально умный указатель или что-то немного умнее, чем умный указатель, который понимает текст.
В этом случае я бы попросил метод вернуть null для несуществующего пациента.
Я склонен предпочитать использовать исключения для содействия серьезному ухудшению качества работы, когда возникает проблема с самой системой.
В данном случае, вероятно, это mosdt:
- опечатка в идентификаторе пациента, если он был введен в форму поиска,
- ошибка ввода данных или
- проблема рабочего процесса в том, что запись пациента еще не введена.
Следовательно, возвращается значение null, а не исключение.
Если бы возникла проблема с обращением к базе данных, то я бы попросил метод вызвать исключение.
Редактировать: Только что увидел, что идентификатор пациента в подписи был целым числом, спасибо Стивену Лоу, поэтому я исправил свой список причин.
Однако мой основной тезис об определении того, когда следует использовать исключения (для системных ошибок) по сравнению с другими методами возврата ошибки (для простых опечаток при вводе данных), остается в силе.ИМХО.
HTH
ваше здоровье,
Роб
В простой ситуации, подобной этой, 1. этого более чем достаточно. Вы можете захотеть реализовать что-то вроде метода обратного вызова, который клиент вызывает, чтобы узнать, почему он вернул значение null. Просто предложение.
принимая ваше описание за чистую монету, вам, вероятно, нужно и то, и другое:
- неверные идентификаторы - это ошибки / исключения, как указал Адам, но
- если вам предоставлены идентификаторы в другом месте, которые могли исчезнуть, вам понадобится метод запроса, чтобы проверить их наличие
Если я правильно прочитал ... Когда вы звоните пациенту (100), он возвращает ссылку на объект для пациента с идентификатором 100. Если ни один пациент с идентификатором 100 не существует, я думаю, что он должен вернуть ноль. Исключения превышают IMO, и этот случай не требует этого. Функция просто вернула ноль. Это не создало какой-либо случай с ошибкой, который может привести к сбою вашего приложения (если, конечно, вы не обработали этот нуль и передали его какой-то другой части вашего приложения).
Я определенно хотел бы, чтобы эта функция возвращала 'null', особенно если бы это было частью какого-то поиска, когда пользователь будет искать пациента с определенным идентификатором, и если ссылка на объект в итоге окажется нулевой, он просто заявит, что пациент с таким идентификатором не существует.
Брось исключение.
Если вы вернете ноль, напишите такой код:
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. Кроме того, это будет означать два запроса к базе данных вместо одного. Обычно лучше просто попробовать позвонить и обработать исключение.
Обратите внимание, что ситуация отличается для запросов, которые возвращают несколько значений, например, в виде списка; здесь уместно вернуть пустой список, если совпадений нет.