0

我在 Doctrine 模型上创建了一个方法来将相关对象添加到集合中,但是我想在将重复对象添加到该集合时抛出异常。

这是测试:

public function testFluentInterface(  )
{
  $sport = new Sport();
  $this->assertSame($sport, $sport->addCode('ANY'),
    'Expected Sport to implement fluent interface.'
  );
}

public function testCannotAddSameCodeMoreThanOnce(  )
{
  $code = 'BAZ';

  $sport = new Sport();
  $sport->addCode($code);

  try
  {
    $sport->addCode($code);
    $this->fail(
      'Expected error when trying to add the same code to a sport more than once.'
    );
  }
  catch( /*SomeKindOf*/Exception $e )
  {
  }
}

起初,我认为OverflowException在这种情况下抛出 an 可能是合适的,但我不确定“这个值已经存在”是否与“这个容器已满”相同:

将元素添加到完整容器中时引发异常。

UnexpectedValueException,但这似乎更适用于类型不正确的变量:

如果一个值与一组值不匹配,则会引发异常。通常,当一个函数调用另一个函数并期望返回值是某种类型或值时,会发生这种情况,不包括算术或缓冲区相关的错误。

我总是可以使用LogicException,但这对于这个用例来说似乎有点通用:

表示程序逻辑错误的异常。这种异常应该直接导致代码中的修复。

这里有更好的选择吗?我的观察正确吗?尝试将重复项添加到必须包含唯一值的集合时,最合适的 SPL 异常是什么?

4

3 回答 3

1

没有真正适合的 SPL 例外。您最好创建自己的异常:

/**
 * Raised when item is added to a collection multiple times. 
 */
class DuplicateException extends RuntimeException {}; 

在这种情况下抛出异常似乎有点激烈,有点相当于每当您将相同的值分配给数组中的相同键时抛出异常。您是否考虑过使用返回值而不是异常来检测这种情况?

于 2011-11-29T17:22:53.873 回答
0

例如,Java 集合框架(即 Set)中的许多类在这种情况下不会抛出异常,它们返回 false。

就我个人而言,我会那样做。

于 2011-11-29T17:10:49.257 回答
0

我想得越多——尤其是考虑到托尼和弗朗西斯各自的答案——我就越想知道在这种情况下抛出异常是否有用。

根据 Toni 的观点,许多班级已经以不同的方式处理这种情况。尽管在这种情况下返回“失败”值不是一种选择(Sport该类实现了一个流畅的接口),但尝试将对象添加到集合中两次不一定是“例外”情况。

Francis 还提出了一个很好的观点,即 PHP 中的其他构造(例如数组)也不关心这类事情。将重复项添加到集合中时抛出异常将是前所未有的行为,可能会导致后续的可维护性问题。

此外,抛出异常可能被认为与流畅接口的概念不一致。如果开发人员必须通过条件检查来分解链,那么允许开发人员在实例上链接方法有什么意义呢?

换句话说,如果目标是这样的:

$sport
  ->addCode($codeA)
  ->addCode($codeB)
  ->setSomeOtherProperties(...)
  ->save();

那么强迫开发人员必须这样做是没有意义的:

if( ! $sport->hasCode($codeA) )
{
  $sport->addCode($codeA);
}
// etc. - Why bother having a fluent interface if it's unsafe to use it?

上面的代码特别有问题,因为无论如何addCode()都会调用hasCode(),这实际上迫使开发人员调用重复计算。

并且使用 try/catch 也好不到哪里去:

try
{
  $sport
    ->addCode($codeA)
    ->addCode($codeB)
    ->setSomeOtherProperties(...)
    ->save();
}
catch( LogicException $e )
{
  // $sport is now in an unknown state.
}

尝试从LogicException上述块中的 a 恢复是不切实际的,在这里甚至没有必要;尝试添加两次代码不足以阻止Sport对象被持久化。

于 2011-11-29T19:49:44.943 回答