2013-12-09 3 views
2

У меня есть код для сортировки вектора объектов. Если какой-либо из объектов недействителен, я хочу немедленно прекратить сортировку и сообщить об ошибке. В ошибке я хочу включить описание недопустимого объекта (независимо от того, какой из них существует).Почему выбрасывается исключение, считающееся плохим дизайном?

Это мой код (не полный, но я надеюсь, что вы можете следовать за мной):

int sortProc(const Bulk & b1, const Bulk & b2) { 
    if (!b1.isValid()) throw b1; 
    if (!b2.isValid()) throw b2; 
    return b1.compareTo(b2); 
} 

vector<Bulk> * items = getItems(); 
try { 
    sort(items->begin(), items->end(), sortProc); 
} catch (const Bulk & item) { 
    cout << "Cannot sort item: " << item.description(); 
} 

Теперь я немного неуверенный моего кода, потому что я слышал, что все исключения должны наследоваться класс исключения, и считается ошибочной практикой бросать объекты, которые не являются исключениями, но я действительно не понимаю, почему. Мой код выше работает, что-то не так с ним? Это серьезный вопрос, поэтому, если вы увидите какие-то проблемы, я был бы рад узнать. Я не ищу моральных проблем.

+3

Проблема в том, как бы [другие] уловить ее? Особенно, если они связаны с статической библиотекой, а 'class Bulk' не определяется в каких-либо файлах заголовков. –

+1

Учтите, что то, что вы называете «моральными проблемами», существует по какой-то причине. –

+4

Важное значение имеет кодирование моральных проблем. Смотрите и наслаждайтесь: http://thecodelesscode.com/case/1 –

ответ

7

Я не ищу моральных проблем.

Вы не можете задавать вопрос стиля, а затем запрещать все ответы на основе «моральных проблем», если вы планируете выяснить это.

Некоторые люди считают, что бросание только объектов типов, полученных std::exception, обеспечивает согласованность интерфейса, поскольку вы можете вызывать .what() на всех из них и объединить их на верхнем уровне вашей программы. Вы также можете гарантировать, что другие единиц перевода — те, кто никогда не слышал о вашем классе Bulk — смогут получить исключение, если они хотят (если только как std::exception).

  • Является ли ваша программа неправильно? №

  • Работает ли это? Да, конечно.

Но иногда просто «работа» не считается достаточным, и нам нравится быть немного более аккуратным о вещах.

Это действительно так ...

+0

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

+0

@Joker_vD Я даже не думаю, что это правильно. Как я уже сказал в своем «решении», все, что не является исключением, не является исключением и поэтому не должно быть выбрано. Это больше касается логики программы. Я лично против использования таких «трюков» практически для любого программирования, если нет никакого способа обойти. Одним из примеров будет использование пустого указателя структуры в конце части объявленной памяти в ядре. Ни в коем случае этот трюк не может этого добиться. – Xephon

1

Это не про мораль. Речь идет не о функциональности. Дело не в правильности. Речь идет о логике вашего кода.

Помните, что код отражает то, как вы думаете. Ясный мыслящий разработчик никогда не бросит «исключение», которое действительно не является причиной исключения, которая просто путает логику.

Joker_vD также прав относительно производительности кода, но я не думаю, что вы еще там.

1

Я вижу (может быть, серьезную) проблему с массовым переносом данных ввода и отказа. Наличие производной формы BulkException std :: exception - это очиститель!

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

Ошибка BulkException может собрать дополнительную информацию (например, трассировку стека), которая бесполезна для нормальной работы.

0

Вот код очень похож на ваш, за исключением того, что не имеет контроля потока с произвольным доступом:

std::unordered_set<std::string> errors; 
auto sortProc = [&errors](const Bulk & b1, const Bulk & b2)->int { 
    if (!b1.isValid()) errors.insert(b1.description()); 
    if (!b2.isValid()) errors.insert(b2.description()); 
    if (!b1.isValid()||!b2.isValid()) 
    return b1.isValid()<b2.isValid(); 
    return b1.compareTo(b2); 
} 

vector<Bulk> * items = getItems(); 
sort(items->begin(), items->end(), sortProc); 
if (!errors.empty()) { 
    std::cout << "Unsortable items found while sorting:\n"; 
    for (auto const& e : errors) { 
    std::cout << e << "\n"; 
    } 
} 

Я предпочитаю избежать случайного контроля потока доступа, кроме исключительных обстоятельств. Функция sort может продолжать игнорировать отказ элемента в isValid, поэтому он должен по умолчанию вместо того, чтобы управлять потоком управления на основе исключаемого интерфейса.

Хотя заманчиво использовать исключения как способ возврата «второго возвращаемого значения», результат заключается в том, что интерфейс вашего кода становится скрытым и сочетается с деталями реализации как вашего кода, так и кода, который использует ваш код ,

В зависимости от программы поистине исключительные обстоятельства могут отличаться от «отказа от распределения памяти» до «сбоя жесткого диска», где нет возможности восстановить или иметь ошибку в коде, не обращая внимания.

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

+0

Так что, например, «управление потоком случайного доступа» означает «ранний выход на отказ»? –

+1

@SteveJessop nope, я имею в виду «одетый goto», где вы переходите из одного места в код в другой, без каких-либо признаков в структуре промежуточного кода, который это произойдет. Ранний выход на неудачу - это великолепно, вы должны разработать свои функции, чтобы иметь четкие коды кода, которые позволяют это, когда это того стоит! Использование исключений для случаев неисключительного отказа ... менее полезно для меня. Если 'sort' * ожидается * слишком часто, вы не должны использовать исключения. Если ожидается, что он не будет терпеть неудачу часто, то отказ от раннего выхода не является большой стоимостью сейчас? – Yakk

+1

Я признаю, что это широко распространено, но я не разделяю убеждение, что уместность исключений зависит от частоты возникновения события (и что «исключительный» означает что-то вроде «редких и плохих»). Я также не думаю, что исключение больше или меньше похоже на goto в зависимости от того, насколько редко это событие или насколько оно плохое. Это случай, который следует учитывать при анализе потока управления кодом (или если он не записывает код для его обработки). В этом примере, если некоторые из элементов являются «несортируемыми», тогда «сортировка» невозможна и не должна возвращаться. –

3

Почему выбрасывается исключение, если не считать плохой дизайн?

Потому что редко системы существуют в вакууме.

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

Рассмотрим последнюю часть тщательно:

без учета местности этих границ.

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

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

Как это обычно делается в C++, путем получения всех объектов исключения из std::exception. Таким образом, обработчик в обширной части кода - возможно, даже код, о котором вы даже не мечтали написать, - может, по крайней мере, сообщить об исключительном случае, прежде чем программа встретится с кончиной. Возможно, он сможет справиться с ситуацией и позволить программе жить, но это часто невозможно.

0

Хороший дизайн о качестве.Правильно ли ваш код? Минимизирует ли он зависимости от внешних объектов? можно ли его повторно использовать? Это понятно? Устраняет ли это избыточность? Вы можете проверить его? Могут ли другие люди прочитать это и узнать, что он делает? Этот последний вопрос действительно тяжелый, потому что вы не другие люди - вы - автор.

Смежные вопросы