试图在集合中添加副本 - 抛出什么样的异常?

时间:2011-11-29 17:03:26

标签: php exception spl

我正在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可能是合适的,但我不确定“此值是否已存在”与“此容器已满”相同:

  

将元素添加到完整容器时抛出异常。

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

  

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

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

  

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

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

3 个答案:

答案 0 :(得分:1)

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

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

在这种情况下抛出异常似乎有点激烈,有点等同于每次为数组中的相同键赋予相同的值时抛出异常。您是否考虑使用返回值而不是异常来检测此情况?

答案 1 :(得分:0)

作为示例,Java Collection Framework中的许多类(即Set)在这种情况下不会抛出异常,它们返回false。

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

答案 2 :(得分:0)

我对它的思考越多 - 特别是考虑到Toni和Francis各自的答案 - 我越想知道在这种情况下抛出异常是否有用。

Per Toni的观点是,许多课程已经以不同的方式处理这种情况。虽然在这种情况下返回“失败”值不是一个选项(Sport类实现了一个流畅的接口),但尝试将对象添加到集合两次并不一定是“例外”的情况。

弗朗西斯也提出了一个很好的观点,即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恢复是不切实际的,在这里甚至不需要它;尝试两次添加代码不足以阻止Sport对象被保留。