我正在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异常是什么?
答案 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
对象被保留。