【问题标题】:Exceptions throwing for invalid argument inside a method方法内的无效参数抛出异常
【发布时间】:2014-01-25 11:25:22
【问题描述】:

我正在学习异常处理。我已经知道如何使用它们,但是我不知道什么时候可以使用它们,因为很少有教程能告诉你有关这方面的任何见解。我的代码:

// 0-index part of the url
public function part($Part)
  {
  if (!is_numeric($Part))
    throw new Exception('The argument for $Url->part() should be numeric');

  $Part = (int) $Part;

  if ($Part < 0)
    throw new Exception('The argument for $Url->part() should be positive');

  if ($Part > count($this->parts))
    return false;

  return $this->parts[$Part];
  }

感觉我的代码异常太多了。它是检索当前 url 及其某些部分的代码的一部分。例如,/this/is/a/test/ 将在 $this-&gt;parts 中保存为 array('this', 'is', 'a', 'test')

我是否在该方法中使用了太多异常,从而影响了可读性?如果出现任何问题,我是否应该只使用一个异常,使调试稍微困难但更容易阅读源代码?

这是问题中命名的更通用的异常:

// 0-index part of the url
public function part($Part)
  {
  if (!is_numeric($Part) || intval($Part) < 0 || intval($Part) > count($this->parts))
    throw new Exception('The argument for $Url->part() is not correct');

  return $this->parts[(int) $Part];
  }

【问题讨论】:

  • 您愿意处理和引发越界异常吗?
  • 我通常不会使用那么多异常,但不是因为可读性。如果你这样做,你必须在每次调用part() 时处理(try/catching)你的错误。我个人会在出现任何错误时返回 false,并且在调用 part() 时,请在继续之前检查是否有有效的返回。
  • OP,回答“我什么时候应该使用...”异常处理问题,您会问“我什么时候知道它失败的原因很重要?”。 catch all 'return false' 或 'return null' 是草率的代码,除非不需要知道其他任何内容。 IE 像 isValid() 这样的函数应该返回 true/false,但是当某些东西被微调时,比如一个值必须是一个特定范围内的数字,那么了解失败的原因很重要。
  • 验证用户输入不是异常情况:异常应该用于异常(“例如数据库已消失”)而不是常规表单验证,其中无效的用户输入是正常而不是异常情况
  • 即便如此,它仍然不是“整个互联网崩溃”这样的异常情况

标签: php oop exception object methods


【解决方案1】:

实际上,您的问题与基于意见的 问题非常接近(因此,如果没有非建设性的讨论,可能很难正确回答)。但是,有些事情需要记住。

首先,提出只是异常 可能会得到改进 - 因为 PHP 中有标准的exceptions。扔掉它们肯定会提高可读性。例如,您的代码接受一些应该是数字的参数。如果不是 - 那么它是 invalid argument - 因此,可能会触发相应的异常。接下来,您的 parts 是一个数组 - 您想检查是否存在传递的偏移量。如果不是,那么它是越界异常。所以你的代码可能看起来像:

public function getPart($part)
{
   if(!is_numeric($part))
   {
      throw new InvalidArgumentException('Invalid part number passed');
   }
   if(!array_key_exists($part, $this->parts))
   {
      throw new OutOfBoundsException('Offset '.$part.' not found in parts');
   }
   return $this->parts[$part];
}

-这对于数字偏移似乎是合理的。但是,如果您的结构包含更复杂的数据,那么您可能需要引发 逻辑异常(指出传递参数的逻辑结构或当前结构存在错误)。

在一般情况下 - 这一切取决于情况和逻辑。没有没有灵丹妙药 - 它与设计有关,所有解决方案都是相对的并受特定情况的约束。

再一次,这是非常基于意见的(例如,我从将您的方法重新命名为getPart() 开始 - 因为对我来说方法是一个应该命名为动作名称的实体,不仅仅是事物名称)。而且,更多 - 在返回某些东西时尽量避免混合不同的类型(例如您的示例中的false)。它会导致不可靠的行为。最好抛出异常,但保持函数/方法返回类型相同。

【讨论】:

  • 我稍微更改了标题以尽量减少opinion-based。 +1,因为这是一个完全让我满意的好答案。它使代码比我原来的代码更具可读性(使用语义异常)。但是,这又给我带来了另一个问题,我自己去研究一下,这不就是使用了太多的异常类,增加了整体代码的复杂度吗?
  • 你首先需要确定你在代码复杂度下是什么意思
  • 我的意思是需要编写、测试和理解更多的代码行。 (想法来自Code Complete 2nd edition,第 5.2 节)。
  • 代码行少并不意味着可读性更好。总会有妥协。但是,如果你确切地知道每一行的作用,如果你确定 - 你的代码有多可靠和稳定 - 那么,我认为,找到妥协并不难
  • 不,我的意思是如果代码在整个产品中的代码行数较少,同时提供类似的功能,代码的复杂性就会降低。但是,我同意妥协,我接受你的回答,因为它解决了我最初的问题,使代码更具可读性(和/或有意义),同时提供有价值的调试信息,以防出现问题。
【解决方案2】:

@Mark,我要验证的不是用户输入,而是程序员的输入。

是的,这太过分了。如果你想检查 programmer 输入的完整性,用户 assert

没有合理的方法来处理由输入错误的代码、疯狂的参数或不正确地使用库所产生的异常,而这正是您所要防范的。使用异常作为防范此类错误的机制没有任何价值。

当意外发生时,您会引发异常,但您无法优雅地处理它。异常将控制权传递回堆栈,理想情况下,可以处理那种类型的错误,而不会导致程序失败。在运行时确实没有办法处理您要防范的那种错误。如果有人给你的库输入错误,以至于你的库只能中止,那么我们此时能做的最好的事情就是给用户尽可能多的调试信息并退出; assert 是为此专门设计的。

请注意,我认为即使是assert 通常也太过分了。您使用的是duck-typed 语言;谁在乎你的论点类型?只要它们响应您将要对其调用的所有方法,您的代码就应该乐于接受 任何 类型的对象。我会考虑在参数上使用assert 的唯一情况是,当我编写库代码并且我知道我想要生成比 PHP 更友好的错误消息时。

【讨论】:

  • 其实我打字太快了,我想验证的不仅仅是程序员的输入,还有很多东西,主要是程序员从其他类的输入,数据库值和$_SESSION变量。不过assert()我没学过也没用过,所以在选择答案之前先了解一下。
  • assert() 似乎只将问题处理交给外部的自定义回调函数,其方式类似于抛出自定义异常而不捕获它。另一方面,在文档中它指定Assertions should not be used for normal runtime operations like input parameter checks. As a rule of thumb your code should always be able to work correctly if assertion checking is not activated.,这正是我想要做的。
  • 范围检查(&gt;= 0&lt;= length 等),不,不要断言这些。但是您正在检查对象的 type,一旦代码运行,就永远不会改变。没有一组用户输入应该改变你的参数的types。这是代码开发过程中出现问题的一个案例,而这正是 assert 想要捕捉的。
猜你喜欢
  • 1970-01-01
  • 2013-11-29
  • 1970-01-01
  • 2021-04-15
  • 2011-04-04
  • 2019-04-09
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多