代码是否应该阻止逻辑上无效的呼叫,即使不会造成伤害?

时间:2009-12-14 11:50:50

标签: exception error-handling

这一直困扰着我。

让我们设想一个代表资源的类,为了能够使用这个资源,需要首先在其上调用'Open'方法,否则将抛出InvalidOperationException。

我的代码是否还要检查是否有人试图打开已经打开的资源,或者关闭已经关闭的资源?

即使没有造成伤害,代码是否应该阻止逻辑上无效的调用?

我认为以这种方式编程有助于在另一方编写更好的代码,但我觉得我可能会承担太多责任并影响可重用性。

你们有什么想法?

编辑:

我不认为这可以被称为防御性编程,因为它不会让任何可能的错误使用滑动,并且将抛出另一个InvalidOperationException。

7 个答案:

答案 0 :(得分:6)

这称为defensive programming。这是一个很好的编程实践,因为你确保你的应用程序不会因不当行为而崩溃。

在调用另一个方法之前应首先调用某个方法,这不是一个好的编程习惯。它增加了很多复杂性,这更好地由类本身处理。

这称为sequential coupling。这篇维基百科文章说,如果这是一个不好的做法,它取决于上下文,但它不应该在处理不当时崩溃。有时需要抛出异常以使事情变得清晰。

答案 1 :(得分:3)

这实际上取决于班级实际做了什么。在某些情况下,静默失败是个好主意(例如,您希望DVD播放器继续工作,如果打开已经打开的DVD托盘则不显示错误消息),在其他情况下,您需要尽可能多的信息(例如, ,如果飞机试图关闭据说已经关闭的门,则出现问题并应提醒飞行员。

在大多数情况下,在执行逻辑上无效的操作时抛出错误对开发人员很有用,因此实现这些异常取决于谁将使用代码。如果它在内部用于一个应用程序,那么它并不重要。但如果它被许多不同的项目或开发人员使用,那么我会调查它。

答案 2 :(得分:2)

如果您的示例确实如此,则可能应该由类的构造函数调用Open功能。

如果你考虑C ++ iostream库(它被广泛使用并被认为是一个很好的例子),你可以调用流类的任何操作,无论它是否打开。如果无法执行操作,被调用的函数将只返回某种失败指示符。当然,函数必须测试流状态才能执行此操作。

您不能做的是允许您的程序默默接受任何旧输入作为参数。例如,这将是strlen()

的破坏实现
int strlen( const char * s )
{
   if ( s == 0 )
   {
      return 0;     // bad
   }
   else
   {
      // calculate length not shown
   }
} 

因为它会在不引起麻烦的情况下输入错误的输入 - 它应该抛出异常或使用assert(),具体取决于您的确切开发理念。

答案 3 :(得分:1)

在确定您的代码中应该有多少安全检查以确定组织的最佳成本/效益比时,无法替代品味,才能和经验。

优质的API应该是万无一失的,并引导用户提供适当的警告。

有时,安全预防措施可能会影响性能。性能是编程中最直观的事情之一。只有在表现真正重要时才小心优化。

答案 4 :(得分:1)

如果这是您正在发布的公共SDK的一部分,那么公开的API调用应该具有强大的验证功能。它将帮助您的“用户”(他们是开发人员)并确保您不会支持您从未打算支持的使用。

否则,我不会添加此类支票。我认为它们使代码更难阅读,并且很少测试这些检查。在过去,我会添加很多这样的代码,以确保我的代码不会做错事。现在我编写单元测试来验证我的代码做了正确的事情。区别?我认为测试更易于维护,更易读,并且不会使您的生产代码混乱。

答案 5 :(得分:1)

在打开已经打开的文件的情况下,它取决于知道请求的效果,例如它是否会重置当前的读取位置。

如果关闭已关闭的文件,请将其视为请求将文件置于已知状态。代码不必执行任何操作,只需实现所需的状态,因此代码可以返回成功条件。如果存在某种需要处理的文件缓冲或者可能是相互关联的资源进行协调,例如调制解调器/串行端口或打印机/假脱机程序,则情况并非如此。

退一步思考问题的结果,包括任何副作用。

我们曾在显示的应用菜单上放置了“退出”链接,无论您的登录状态如何。为什么?因为它只需要一个简单(非常短)的方法来处理从登录屏幕返回登录屏幕并保存大量检查以处理跟踪登录状态,所以只有当“注销”菜单项显示时才显示你已经登录了。

答案 6 :(得分:1)

应始终以调试模式向用户报告逻辑无效调用。

在发布模式下编译时,您的代码不应抛出任何不需要的异常或执行任何可能危及整个应用程序的异常。 我个人更喜欢使用某种日志文件,并且记录这些逻辑上无效的调用肯定不会造成任何伤害(至少在性能不重要时)