哪种合同(按合同设计)更好?
-
03-07-2019 - |
题
假设我有一个方法
public Patient(int id)
{
----
}
返回给定 id 的 Patient 对象..我可以用两种方式定义合同
- 如果患者不存在,方法将返回 null
- 如果患者不存在,方法将抛出异常。在这种情况下,我还将定义一个查询方法,如果数据库中存在患者,则返回 true,否则返回 false...
我应该使用哪个合同?还有其他建议吗?
更新:也请评论这个案例...如果它不是数据库分配的 ID,而是用户在 UI 中输入的内容。比如SSN..那么哪个更好..
史蒂夫对空模式的评论,我认为是有效的:在这里可能不是一个好主意,因为当 ID 不存在时立即知道是非常有用的。
我还认为这里的空模式有点重
Rob Wells 对因 ID 错误而抛出异常的评论:我不认为患者姓名中的拼写错误是特殊情况”恕我直言
解决方案
请记住,“通过电线”到另一层(无论是数据库还是应用程序服务器)是您可以执行的最昂贵的活动之一 - 通常网络调用将比内存调用花费几个数量级。
因此,在构建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);
显然,可以通过调用FindPatient()来构建GetExistingPatient()。
这允许您的调用代码获取适当的行为,如果出现错误则抛出异常,并避免在不需要的情况下处理异常。
其他提示
另一种选择是空对象模式。
你应该抛出异常。如果您的 id
没有指向有效的患者,那么它来自哪里?可能发生了非常糟糕的事情。这是一个特例。
编辑:如果您正在执行基于整数的检索以外的操作,例如基于文本的搜索,则返回 null
就可以了。特别是因为在这种情况下,您将返回一组结果,这些结果可能不止一个(多个患者姓名相同,出生日期相同,或者您的标准是什么)。
搜索功能应与检索功能具有不同的合约。
取决于:
如果您认为正常操作会导致数据库编号与数据库中的文件不匹配,则应返回空(NULL)记录。
但是如果你期望给定的ID应该总是打到一个记录,那么当找不到一个(这应该是罕见的)然后使用异常。
其他问题如数据库连接错误应该会产生异常。
正如您在正常情况下所期望的那样,对DB的查询始终有效(尽管它可能返回0条记录)。
P.S。我不会返回指针。 (谁拥有指针?)
我会返回一个可能有或没有记录的对象。但是你可以在内部记录存在的内存。可能是智能指针或比理解cotext的智能指针更聪明的东西。
对于这种情况,我会让方法为不存在的患者返回null。
当系统本身出现问题时,我更倾向于使用异常来帮助降低graeful降级。
在这种情况下,很可能是:
- 患者身份证中的拼写错误(如果已输入搜索表单),
- 数据输入错误,或
- 工作流程问题,即患者的记录尚未输入。 醇>
因此,返回null而不是异常。
如果联系数据库时出现问题,那么我会让该方法引发异常。
编辑:刚看到签名中的患者ID是一个整数,感谢Steven Lowe,所以我已经更正了我的原因列表。
关于描述何时使用异常(对于系统错误)与其他返回错误的方法(对于简单的数据输入拼写错误)的基本观点仍然存在。 IMHO。
HTH
欢呼声,
罗布
在这样的简单情况下1.似乎绰绰有余。您可能希望实现类似于客户端调用的回调方法,以了解它返回null的原因。只是一个建议。
从表面上看你的描述,你可能需要两者:
- 正如 Adam 指出的那样,坏 ID 是错误/异常,但是
- 如果您在其他地方获得的 ID 可能已消失,则需要使用查询方法来检查它们
假设我正确阅读了...... 当您致电Patient(100)时,它将返回ID为100的患者的对象参考。 如果不存在id为100的患者,我认为它应该返回null。例外是过度使用IMO,这种情况并不需要它。该函数只返回null。它没有创建一些可能导致应用程序崩溃的错误情况(当然,除非您最终没有处理该null并将其传递给应用程序的其他部分)。
我肯定会让该函数返回'null',特别是如果它是某些搜索的一部分,用户将搜索具有特定ID的患者,并且如果对象引用最终为null,则它将简单地说明没有患有这种身份的患者。
抛出异常。
如果返回null,则代码如下:
Console.WriteLine(Patient(id).Name);
如果id不存在,会因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的呼叫之间删除患者。此外,这将意味着两个数据库查询而不是一个。通常,最好只是尝试调用,并处理异常。
请注意,返回多个值的查询的情况不同,例如作为清单;在这里,如果没有匹配则返回空列表是合适的。