Чи є повторне відкидання винятком, що протікає абстракцією?


12

У мене є метод інтерфейсу, який заявляє, що в документації він викине певний тип винятку. Реалізація цього методу використовує щось, що кидає виняток. Внутрішній виняток перехоплюється, а виняток, оголошений договором інтерфейсу, викидається. Ось невеликий приклад коду для кращого пояснення. Він написаний на PHP, але досить простий у дотриманні.

// in the interface

/**
 * @return This method returns a doohickey to use when you need to foo
 * @throws DoohickeyDisasterException
 */
public function getThatDoohickey();

// in the implementation

public function getThatDoohickey() {

    try {
        $SomethingInTheClass->doSomethingThatThrowsAnException();
    } catch (Exception $Exc) {
        throw new DoohickeyDisasterException('Message about doohickey failure');
    }

    // other code may return the doohickey

}

Я використовую цей метод, щоб спробувати не допустити протікання абстракції.

Мої запитання такі: Чи передав би викинутий внутрішній виняток, оскільки попередній виняток витікав би абстракцію? Якщо ні, чи було б доцільно просто повторно використати повідомлення попереднього винятку? Якщо це буде витікати абстракцію, ви могли б дати деякі поради щодо того, чому ви вважаєте, що це робить?

Щоб уточнити, моє запитання передбачає перехід до наступного рядка коду

throw new DoohickeyDisasterException($Exc->getMessage(), null, $Exc);

Винятки повинні стосуватися причин, а не про те, хто їх кидає . Якщо ваш код може мати "a" file_not_found, то він повинен кинути "a" file_not_found_exception. Винятки не повинні залежати від бібліотеки.
Mooing Duck

Відповіді:


11

Мої запитання такі: Чи передав би викинутий внутрішній виняток, оскільки попередній виняток витікав би абстракцію? Якщо ні, чи було б доцільно просто повторно використати повідомлення попереднього винятку?

Відповідь: "це залежить".

Конкретно, це залежить від винятку, і що це означає. По суті, його повторне скидання означає, що ви додаєте це до свого інтерфейсу. Ви зробили б це, якби ви написали код, який ви самі називаєте, чи це просто, щоб уникнути ребрендингу винятку?

Якщо ваш виняток включає відстеження до першопричини у названому коді, який може просочувати дані поза абстракцією - але, як правило, я вважаю, що це прийнятно, оскільки виняток обробляє абонент (і ніхто не цікавиться, де це прийшов) або показаний користувачеві (і ви хочете знати першопричину для налагодження.)

Однак часто допускається виключення через дуже розумний вибір реалізації, а не проблема абстрагування. Просто запитайте: "чи кину я це, якщо код, який я дзвоню, не став?"


8

Строго кажучи, якщо «внутрішнє» виняток показує нічого про внутрішню роботу реалізації, ви є протечками. Тож технічно завжди слід все ловити і кидати власний тип виключення.

АЛЕ

Проти цього можна зробити декілька важливих аргументів . Дуже помітно:

  • Виняток - виняткові речі ; вони багато в чому порушують правила «нормальної роботи», тому можуть також порушувати інкапсуляцію та абстракцію на рівні інтерфейсу.
  • Користь від осмисленого трасування стека та інформації про помилки в точці часто переважає правильність OOP, доки ви не просочите внутрішню інформацію про програму через бар'єр інтерфейсу (що буде розголошенням інформації).
  • Перенесення кожного внутрішнього винятку у відповідний тип зовнішнього винятку може призвести до безлічі марних кодів котла , що вражає цілі цілі винятків (а саме: реалізувати поводження з помилками, не забруднюючи регулярний потік коду при використанні помилки котла).

Зазначене, вилучення та обгортання винятків часто має сенс, якщо тільки для додавання додаткової інформації до ваших журналів або для запобігання витоку внутрішньої інформації для кінцевого користувача. Поводження з помилками все ще є видом мистецтва, і важко звести їх до кількох загальних правил. Деякі винятки мають бути заграні, інші не повинні, а рішення також залежить від контексту. Якщо ви кодуєте інтерфейс користувача, ви не хочете нічого дозволити користувачеві без фільтру; але якщо ви кодуєте бібліотеку, яка реалізує деякий мережевий протокол, певні винятки, мабуть, правильно зробити (адже, якщо мережа в мережі, ви можете мало що зробити, щоб покращити інформацію або відновити і продовжити ; переклад NetworkDownExceptionна FoobarNetworkDownException- просто безглуздий суть).


6

Перш за все, те, що ви тут робите, - це не викидання виключення. Повторне ставлення було б буквально кинути такий же виняток, як і цей:

try {
    $SomethingInTheClass->doSomethingThatThrowsAnException();
} catch (Exception $Exc) {
    throw $Exc;
}

Очевидно, це трохи безглуздо. Повторне втручання - це обставини, коли ви хочете випустити деякі ресурси або записати виняток або що-небудь ще, що ви хочете зробити, не зупиняючи виняток з бульбашки стека.

Те, що ви вже робите, замінює всі винятки, незалежно від причини, DoohickeyDisasterException. Що може бути, а може і не бути тим, що ви маєте намір робити. Але я хотів би запропонувати , що будь-який раз , коли ви зробите це, ви також повинні робити , як ви пропонуєте , і передати вихідне виняток як внутрішнє виняток, тільки в разі , якщо це корисно в трасуванні стека.

Але мова не йде про непрохідні абстракції, це вже інше питання.

Щоб відповісти на запитання, чи може повторне виняток (чи справді невипадкове виключення) може бути хиткою абстракцією, я б сказав, що це залежить від вашого коду виклику. Якщо цей код вимагає знань про абстраговану рамку, то це непрохідна абстракція, тому що якщо ви заміните цю рамку на іншу, вам доведеться змінити код поза вашої абстракції.

Наприклад, якщо DoohickeyDisasterException - це клас із фреймворку, а ваш код виклику виглядає так, то у вас є пробіжна абстракція:

try {
    $wrapper->getThatDoohickey();
} catch (DoohickeyDisasterException $Exc) {
    echo "The Doohickey is all wrong!";
    echo $Exc->getMessage();
    gotoAHappyPlaceAndSingHappySongs();
}

Якщо ви заміните сторонні рамки на інші, які використовують інше ім'я виключення, і вам доведеться знайти всі ці посилання та змінити їх. Краще створити власний клас винятків і загорнути в нього виключення рамки.

Якщо, з іншого боку, DoohickeyDisasterException є вашим власним витвором, то ви не просочуєтесь абстракцій, вам варто більше поставитись до мого першого моменту.


2

Правило, до якого я намагаюся пройти, - це загорнути його, якщо ви його спричинили. Аналогічно, якщо помилка повинна бути створена через виключення з виконання, це має бути якась DoHickeyException або MyApplicationException.

Це означає, що якщо причиною помилки є щось зовнішнє для вашого коду, і ви не очікуєте, що вона вийде з ладу, тоді витончено вийдете з ладу і повторно скиньте її. Так само, якщо це проблема з вашим кодом, то перегорніть його у (потенційно) спеціальний виняток.

Наприклад, якщо файл, як очікується, буде на диску, або очікується, що послуга буде доступною, тоді виправдано поводьтеся з ним та повторно викиньте помилку, щоб споживач вашого коду міг зрозуміти, чому цей ресурс недоступний. Якщо ви створили занадто багато даних для якогось типу, то це ваша помилка загортання та викидання.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.