哪个合同(按合同设计)更好?

时间:2021-09-03 20:08:05

Suppose I have a method

假设我有一个方法

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

that returns Patient object given an id.. I could define contract in 2 ways

返回给定id的Patient对象..我可以用两种方式定义合同

  1. Method would return null if patient does not exist
  2. 如果患者不存在,方法将返回null

  3. Method would throw an exception if patient does not exist. In this case I would also define a query method that returns true if the Patient exist in the database or false otherwise...
  4. 如果患者不存在,方法将抛出异常。在这种情况下,我还将定义一个查询方法,如果Patient存在于数据库中则返回true,否则返回false ...

Which contract should I use? Any other suggestions?

我应该使用哪种合同?还有其他建议吗?

Update: Please comment on this case too... If it is not an database assigned Id and it is something a user enter in UI.. like SSN .. then which one is better..

更新:请对这个案例发表评论......如果它不是数据库分配的ID,那就是用户在UI中输入的东西......比如SSN ..那么哪一个更好..

Comment about Null pattern from Steve that I think is valid: probably not a good idea here, as it would be really useful to know immediately when an ID did not exist.

关于Steve认为有效的Null模式的评论:这里可能不是一个好主意,因为当ID不存在时立即知道它真的很有用。

And I also think Null pattern here would be somewhat heavy weight

我也认为这里的Null模式会有点沉重

Comment from Rob Wells on throwing exception because its bad Id: i don't think a typo in a patient's name is an exceptional circumstance" IMHO

Rob Wells对抛出异常的评论,因为它的错误ID:我不认为患者姓名中的拼写错误是一种特殊情况“恕我直言

9 个解决方案

#1


Keep in mind that going "over the wire" to another tier (whether a database or an application server) is one of the most expensive activities you can do - typically a network call will take several orders of magnitude longer than in-memory calls.

请记住,“通过线路”到另一层(无论是数据库还是应用程序服务器)是您可以执行的最昂贵的活动之一 - 通常网络调用将比内存调用长几个数量级。

It's therefore worth while structuring your API to avoid redundant calls.

因此,在构建API时避免冗余调用是值得的。

Consider, if your API is like this:

考虑一下,如果你的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);

Then you are likely to hit the database twice - or to be reliant on good caching to avoid this.

然后你可能会两次打到数据库 - 或者依赖于良好的缓存来避免这种情况。

Another consideration is this: In some places your code may have a "known-good" id, in other places not. Each location requires a different policy on whether an exception should be thrown.

另一个考虑因素是:在某些地方,您的代码可能具有“已知良好”ID,而在其他地方则不然。每个位置都需要一个关于是否应该抛出异常的不同策略。

Here's a pattern that I've used to good effect in the past - have two methods:

这是我过去常用的一种模式 - 有两种方法:

// 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);

Clearly, GetExistingPatient() can be built by calling FindPatient().

显然,可以通过调用FindPatient()来构建GetExistingPatient()。

This allows your calling code to get the appropriate behaviour, throwing an exception if something has gone wrong, and avoiding exception handling in cases where it is not needed.

这允许您的调用代码获得适当的行为,如果出现错误则抛出异常,并避免在不需要的情况下处理异常。

#2


Another option would be the Null Object pattern.

另一种选择是Null Object模式。

#3


You should probably throw an exception. If you have an id that doesn't point to a valid patient, where did it come from? Something very bad has likely happened. It is an exceptional circumstance.

你应该抛出异常。如果您的身份证不能指向有效的患者,那么它来自哪里?可能发生了非常糟糕的事情。这是一个特例。

EDIT: If you're doing something other than an integer-based retrieval, like a search based on text, then returning null is fine. Especially since in that case you are returning a set of results, which could be more than one (more than one patient with the same name, same birth date, or whatever your criteria is).

编辑:如果你正在做一些基于整数的检索,比如基于文本的搜索,那么返回null就可以了。特别是因为在这种情况下,您将返回一组结果,这些结果可能不止一个(多个患者姓名相同,出生日期相同,或者您的标准是什么)。

A search function should have a different contract from a retrieval function.

搜索功能应与检索功能具有不同的合同。

#4


It depends:

If you consider the normal operation will lead to a pation number not matching a file in the DB then an empty (NULL) record should be returned.

如果您认为正常操作会导致数据库编号与DB中的文件不匹配,则应返回空(NULL)记录。

But if you expect that a given ID should always hit a record then when one is not found (which should be rare) then use an exception.

但是如果你期望给定的ID应该总是达到记录,那么当找不到一个(这应该是罕见的)然后使用异常。

Other things like a DB connection error should generate an exception.
As you expect under normal situations the query to the DB to always work (though it may return 0 records or not).

其他诸如数据库连接错误之类的事情应该会产生异常。正如您在正常情况下所期望的那样,对DB的查询始终有效(尽管它可能返回0条记录)。

P.S. I would not return a pointer. (Who owns the pointer??)
I would return an object that may or may not have the record. But that you can interogated for the existance of the record within. Potentially a smart pointer or somthing slightly smarter than a smart pointer that understands the cotext.

附:我不会返回指针。 (谁拥有指针??)我会返回一个可能有也可能没有记录的对象。但是你可以在内部记录存在的记录。可能是智能指针或比理解cotext的智能指针更聪明的东西。

#5


For this circumstance, I would have the method return null for a non-existent patient.

对于这种情况,我会让方法为不存在的患者返回null。

I tend to prefer using exceptions to assist graeful degradation when there is a problem with the system itself.

当系统本身出现问题时,我倾向于使用异常来帮助降低graeful降级。

In this instance, it is mosdt probably:

在这种情况下,它可能是:

  1. a typo in the patient's ID if it was entered into a search form,
  2. 如果输入搜索表单,患者ID中的拼写错误,

  3. a data entry error, or
  4. 数据输入错误,或

  5. a workflow issue in that he patient's record hasn't been entered yet.
  6. 一个工作流程问题,即患者的记录还没有输入。

Hence, returning a null rather than an exception.

因此,返回null而不是异常。

If there was a problem contacting the database, then I would have the method raise an exception.

如果联系数据库时出现问题,那么我会使用该方法引发异常。

Edit: Just saw that the patient ID in the signature was an integer, thanks Steven Lowe, so I've corrected my list of reasons.

编辑:刚刚看到签名中的患者ID是一个整数,感谢Steven Lowe,所以我更正了我的原因列表。

My basic point about delineating when to use exceptions (for system errors) versus other methods of returning an error (for simple data entry typos) still stands though. IMHO.

关于何时使用异常(对于系统错误)与其他返回错误的方法(对于简单的数据输入拼写错误)的描述,我的基本观点仍然存在。恕我直言。

HTH

cheers,

Rob

#6


In a simple situation like this 1. seems to be more than sufficient. You may want to implement something like a callback method that the client calls to know why it returned null. Just a suggestion.

在这样的简单情况下1.似乎绰绰有余。您可能希望实现类似于客户端调用的回调方法,以了解它返回null的原因。只是一个建议。

#7


taking your descriptiong at face value, you probably need both:

把你的descriptiong带到面值,你可能需要两个:

  • bad IDs are errors/exceptions, as Adam pointed out, but
  • 正如亚当指出的那样,坏ID是错误/异常,但是

  • if you are given IDs elsewhere that might have disappeared, you will need the query method to check for them
  • 如果您在别处可能已经消失的ID,您将需要查询方法来检查它们

#8


Assuming I read that correctly... When you call Patient(100) it will return an object reference for a Patient with an id of 100. If no patient with an id of 100 exists, I think it should return null. Exceptions are overused IMO and this case doesn't call for it. The function simply returned a null. It didn't create some errored case that can crash your application (unless of course, you ended up not handling that null and passed it around to some other part of your application).

假设我正确读取了......当你打电话给Patient(100)时,它将返回id为100的患者的对象参考。如果没有id为100的患者,我认为它应该返回null。例外是过度使用IMO,这种情况并不需要它。该函数只返回null。它没有创建一些可能导致应用程序崩溃的错误情况(当然,除非您最终没有处理该null并将其传递给应用程序的其他部分)。

I would definitely have that function return 'null', especially if it was part of some search, where a user would search for a patient with a particular ID and if the object reference ended up being null, it would simply state that no patient with that id exists.

我肯定会让该函数返回'null',特别是如果它是某些搜索的一部分,用户会搜索具有特定ID的患者,如果对象引用最终为null,则只会声明没有患者那个id存在。

#9


Throw an exception.

抛出异常。

If you return null, code like this:

如果你返回null,代码如下:

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

would fail with a NullReferenceException if the id doesn't exist, which is not as helpful as a say a PatientNotFoundException(id). In this example, it's still relatively easy to track down, but consider:

如果id不存在,则会因NullReferenceException而失败,这不如说是PatientNotFoundException(id)。在这个例子中,它仍然相对容易追踪,但考虑:

somePatient = Patient(id)

// much later, in a different function:

Console.WriteLine(somePatient);

About adding a function that checks whether a patient exists: Note this won't prevent PatientNotFoundExceptions completely. For example:

关于添加检查患者是否存在的功能:请注意,这不会完全阻止PatientNotFoundExceptions。例如:

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

-- another thread or another process could delete the patient between the calls to PatientExists and Patient. Also, this would mean two database queries instead of one. Usually, it's better to just try the call, and handle the exception.

- 另一个线程或另一个进程可以在对PatientExists和Patient的呼叫之间删除患者。此外,这将意味着两个数据库查询而不是一个。通常,最好只是尝试调用,并处理异常。

Note that the situation is different for queries that return multiple values, e.g. as a list; here, it is appropriate to return an empty list if there are no matches.

请注意,返回多个值的查询的情况不同,例如作为清单;在这里,如果没有匹配则返回空列表是合适的。

#1


Keep in mind that going "over the wire" to another tier (whether a database or an application server) is one of the most expensive activities you can do - typically a network call will take several orders of magnitude longer than in-memory calls.

请记住,“通过线路”到另一层(无论是数据库还是应用程序服务器)是您可以执行的最昂贵的活动之一 - 通常网络调用将比内存调用长几个数量级。

It's therefore worth while structuring your API to avoid redundant calls.

因此,在构建API时避免冗余调用是值得的。

Consider, if your API is like this:

考虑一下,如果你的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);

Then you are likely to hit the database twice - or to be reliant on good caching to avoid this.

然后你可能会两次打到数据库 - 或者依赖于良好的缓存来避免这种情况。

Another consideration is this: In some places your code may have a "known-good" id, in other places not. Each location requires a different policy on whether an exception should be thrown.

另一个考虑因素是:在某些地方,您的代码可能具有“已知良好”ID,而在其他地方则不然。每个位置都需要一个关于是否应该抛出异常的不同策略。

Here's a pattern that I've used to good effect in the past - have two methods:

这是我过去常用的一种模式 - 有两种方法:

// 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);

Clearly, GetExistingPatient() can be built by calling FindPatient().

显然,可以通过调用FindPatient()来构建GetExistingPatient()。

This allows your calling code to get the appropriate behaviour, throwing an exception if something has gone wrong, and avoiding exception handling in cases where it is not needed.

这允许您的调用代码获得适当的行为,如果出现错误则抛出异常,并避免在不需要的情况下处理异常。

#2


Another option would be the Null Object pattern.

另一种选择是Null Object模式。

#3


You should probably throw an exception. If you have an id that doesn't point to a valid patient, where did it come from? Something very bad has likely happened. It is an exceptional circumstance.

你应该抛出异常。如果您的身份证不能指向有效的患者,那么它来自哪里?可能发生了非常糟糕的事情。这是一个特例。

EDIT: If you're doing something other than an integer-based retrieval, like a search based on text, then returning null is fine. Especially since in that case you are returning a set of results, which could be more than one (more than one patient with the same name, same birth date, or whatever your criteria is).

编辑:如果你正在做一些基于整数的检索,比如基于文本的搜索,那么返回null就可以了。特别是因为在这种情况下,您将返回一组结果,这些结果可能不止一个(多个患者姓名相同,出生日期相同,或者您的标准是什么)。

A search function should have a different contract from a retrieval function.

搜索功能应与检索功能具有不同的合同。

#4


It depends:

If you consider the normal operation will lead to a pation number not matching a file in the DB then an empty (NULL) record should be returned.

如果您认为正常操作会导致数据库编号与DB中的文件不匹配,则应返回空(NULL)记录。

But if you expect that a given ID should always hit a record then when one is not found (which should be rare) then use an exception.

但是如果你期望给定的ID应该总是达到记录,那么当找不到一个(这应该是罕见的)然后使用异常。

Other things like a DB connection error should generate an exception.
As you expect under normal situations the query to the DB to always work (though it may return 0 records or not).

其他诸如数据库连接错误之类的事情应该会产生异常。正如您在正常情况下所期望的那样,对DB的查询始终有效(尽管它可能返回0条记录)。

P.S. I would not return a pointer. (Who owns the pointer??)
I would return an object that may or may not have the record. But that you can interogated for the existance of the record within. Potentially a smart pointer or somthing slightly smarter than a smart pointer that understands the cotext.

附:我不会返回指针。 (谁拥有指针??)我会返回一个可能有也可能没有记录的对象。但是你可以在内部记录存在的记录。可能是智能指针或比理解cotext的智能指针更聪明的东西。

#5


For this circumstance, I would have the method return null for a non-existent patient.

对于这种情况,我会让方法为不存在的患者返回null。

I tend to prefer using exceptions to assist graeful degradation when there is a problem with the system itself.

当系统本身出现问题时,我倾向于使用异常来帮助降低graeful降级。

In this instance, it is mosdt probably:

在这种情况下,它可能是:

  1. a typo in the patient's ID if it was entered into a search form,
  2. 如果输入搜索表单,患者ID中的拼写错误,

  3. a data entry error, or
  4. 数据输入错误,或

  5. a workflow issue in that he patient's record hasn't been entered yet.
  6. 一个工作流程问题,即患者的记录还没有输入。

Hence, returning a null rather than an exception.

因此,返回null而不是异常。

If there was a problem contacting the database, then I would have the method raise an exception.

如果联系数据库时出现问题,那么我会使用该方法引发异常。

Edit: Just saw that the patient ID in the signature was an integer, thanks Steven Lowe, so I've corrected my list of reasons.

编辑:刚刚看到签名中的患者ID是一个整数,感谢Steven Lowe,所以我更正了我的原因列表。

My basic point about delineating when to use exceptions (for system errors) versus other methods of returning an error (for simple data entry typos) still stands though. IMHO.

关于何时使用异常(对于系统错误)与其他返回错误的方法(对于简单的数据输入拼写错误)的描述,我的基本观点仍然存在。恕我直言。

HTH

cheers,

Rob

#6


In a simple situation like this 1. seems to be more than sufficient. You may want to implement something like a callback method that the client calls to know why it returned null. Just a suggestion.

在这样的简单情况下1.似乎绰绰有余。您可能希望实现类似于客户端调用的回调方法,以了解它返回null的原因。只是一个建议。

#7


taking your descriptiong at face value, you probably need both:

把你的descriptiong带到面值,你可能需要两个:

  • bad IDs are errors/exceptions, as Adam pointed out, but
  • 正如亚当指出的那样,坏ID是错误/异常,但是

  • if you are given IDs elsewhere that might have disappeared, you will need the query method to check for them
  • 如果您在别处可能已经消失的ID,您将需要查询方法来检查它们

#8


Assuming I read that correctly... When you call Patient(100) it will return an object reference for a Patient with an id of 100. If no patient with an id of 100 exists, I think it should return null. Exceptions are overused IMO and this case doesn't call for it. The function simply returned a null. It didn't create some errored case that can crash your application (unless of course, you ended up not handling that null and passed it around to some other part of your application).

假设我正确读取了......当你打电话给Patient(100)时,它将返回id为100的患者的对象参考。如果没有id为100的患者,我认为它应该返回null。例外是过度使用IMO,这种情况并不需要它。该函数只返回null。它没有创建一些可能导致应用程序崩溃的错误情况(当然,除非您最终没有处理该null并将其传递给应用程序的其他部分)。

I would definitely have that function return 'null', especially if it was part of some search, where a user would search for a patient with a particular ID and if the object reference ended up being null, it would simply state that no patient with that id exists.

我肯定会让该函数返回'null',特别是如果它是某些搜索的一部分,用户会搜索具有特定ID的患者,如果对象引用最终为null,则只会声明没有患者那个id存在。

#9


Throw an exception.

抛出异常。

If you return null, code like this:

如果你返回null,代码如下:

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

would fail with a NullReferenceException if the id doesn't exist, which is not as helpful as a say a PatientNotFoundException(id). In this example, it's still relatively easy to track down, but consider:

如果id不存在,则会因NullReferenceException而失败,这不如说是PatientNotFoundException(id)。在这个例子中,它仍然相对容易追踪,但考虑:

somePatient = Patient(id)

// much later, in a different function:

Console.WriteLine(somePatient);

About adding a function that checks whether a patient exists: Note this won't prevent PatientNotFoundExceptions completely. For example:

关于添加检查患者是否存在的功能:请注意,这不会完全阻止PatientNotFoundExceptions。例如:

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

-- another thread or another process could delete the patient between the calls to PatientExists and Patient. Also, this would mean two database queries instead of one. Usually, it's better to just try the call, and handle the exception.

- 另一个线程或另一个进程可以在对PatientExists和Patient的呼叫之间删除患者。此外,这将意味着两个数据库查询而不是一个。通常,最好只是尝试调用,并处理异常。

Note that the situation is different for queries that return multiple values, e.g. as a list; here, it is appropriate to return an empty list if there are no matches.

请注意,返回多个值的查询的情况不同,例如作为清单;在这里,如果没有匹配则返回空列表是合适的。