2011-11-29 1 views
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 быть выброшен в этом случае, но я не уверен, является ли «уже существует это значение» такая же, как «этот контейнер заполнен»:

Исключение, когда вы добавляете элемент в полный контейнер.

Там в UnexpectedValueException, но это, кажется, больше относится к переменным с неправильными типами:

Исключение генерируется, если значение не совпадает с набором значений. Обычно это происходит, когда функция вызывает другую функцию и ожидает, что возвращаемое значение будет иметь определенный тип или значение, не считая ошибки, связанные с арифметикой или буфером.

Я всегда мог использовать LogicException, но это кажется немного родовым для этого случая использования:

Exception, который представляет ошибку в логике программы. Подобные исключения должны непосредственно привести к исправлению в вашем коде.

Есть ли лучший выбор здесь? Правильны ли мои наблюдения? Каким будет наиболее подходящее исключение SPL для броска при попытке добавить дубликат в коллекцию, которая должна содержать уникальные значения?

ответ

0

Чем больше я думаю об этом - особенно в свете соответствующих ответов Тони и Фрэнсиса - тем больше я задаюсь вопросом, полезно ли в этом случае бросать исключение.

Точка точки Тони, многие классы уже обрабатывают эту ситуацию по-разному. Хотя возврат значения «отказ» в данном случае не является опцией (класс 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/поймать не намного лучше:

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

Пытаясь оправиться от LogicException в вышеуказанном блок непрактично, и это не должно быть даже здесь необходимо; попытка добавить код дважды не должна быть достаточной для остановки объекта Sport.

+1

Я не знаю, что делает 'addCode()', но независимо от того, молчали ли вы игнорировать дубликаты или возбуждать исключение, сводится к тому, насколько это опасно. Если вы можете безопасно ничего не делать, когда 'addCode()' вызывается несколько раз с тем же параметром, и это не было бы удивительным поведением для разработчика, то я не думаю, что вам нужно указать это условие вообще, либо с исключение или возвращаемое значение. Если пользователь * действительно * должен знать, он может использовать 'hasCode()'. Если, однако, этот метод более опасен, а сбой - это большая проблема, выведите исключение. В любом случае, это может быть слишком опасно для использования. –

+1

Обратите внимание, что это также изменило бы характер вашего первоначального теста - вы больше не будете тестировать открытый интерфейс, но должны будете проверить внутреннее, возможно, личное состояние объекта, чтобы убедиться, что код не был добавлен дважды. –

0

В качестве примера многие классы в Java Collection Framework (т. Е. Set) не генерируют исключения в таких случаях, они возвращают false.

Лично я бы пошел таким образом.

+0

Спасибо Тони. Это хороший момент. К сожалению, в этом случае это было бы невозможно, так как класс поддерживает свободный интерфейс (т. Е. Код, который вызывает '$ sport-> addCode()', будет ожидать, что возвращаемое значение будет '$ sport'). –

+1

В этом случае я бы выбрал LogicException, потому что другие два немного вводят в заблуждение (опять же, это только личное предпочтение). Хорошая работа и удача! –

+0

Я обновил вопрос, чтобы отметить свободный интерфейс. Еще раз спасибо за ваши предложения. Они подняли некоторые очень важные вопросы о том, как я приближался к проблеме. –

1

Не существует исключения SPL, которое действительно подходит. Вы могли бы быть лучше создавать свои собственные исключения:

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

Бросив исключение в этом случае кажется немного резким, несколько эквивалента бросать исключение, когда вы назначить такое же значение для того же ключа в массиве. Рассматривали ли вы использование возвращаемого значения вместо исключения для обнаружения этого случая?

+0

Спасибо за предложение Фрэнсис. В этом случае класс реализует свободный интерфейс, поэтому код, который вызывает, например, '$ sport-> addCode()', будет ожидать, что возвращаемое значение будет '$ sport'. –

+0

Я обновил вопрос, чтобы отметить свободный интерфейс. Еще раз спасибо за ваши наблюдения. Они подняли некоторые очень важные вопросы о том, как я приближался к проблеме. –