Как стать автором
Обновить

Комментарии 19

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

Что-то мне подсказывает, что в представленной реализации, мне придется каким-то образом проверить на валидность класс а уже в самом исключении. И развивая мысль дальше, исключения должны уметь работать с какими-то определенными классами/интерфейсам.
Не слишком ли специфичными будут исключения?
Если они будут специфичными, то значит идет проверка бизнес-логики, а её лучше ее строить не на исключениях все же. Валидация ввода пользователя — тоже как-то сомнительно.

Везде упоминаемая совместимость на уровне интерфейсов в исключениях, выглядит сковывающим фактором. Хотя есть над чем подумать, по крайней мере над самой идей нагрузить исключения какой-либо полезной логикой.
Валидность класс a может проверить только сам класс a.

Для этого у класса a (а также у b, c и других, для которых это необходимо) пишем метод validate(), но вместо того чтобы вызывать его непосредственно в блоке catch, мы передаем в метод catchException нашего исключения $this и вызываем validate оттуда. Это гарантирует нам, что мы не забудем сделать нужные проверки.

Вообще, я мыслю catchException как шаблонный метод. Таким образом, исключение знает что нужно сделать, а как — это уже не его забота.

Но вы правы, исключения получаются довольно специфичными. Да и сам метод явно не из тех, которые следует применять везде и всюду.
Мне кажется, что те исключения, которые Вы описали, нарушают принцип единственной ответственности (SRP из SOLID): помимо описания типа исключительной ситуации, факта её возникновения, условий (innerException + data) и места (stack trace), такой класс ещё инкапсулирует и логику обработки такой ситуации. Чуть лучше было бы, если тип исключения ссылался на объект типа «обработчик соответствующего исключения», но это приводит к своим неудобствам (к целому ряду). В общем, не изящно.

Кстати, слово «cather» («перехватчик») довольно неточно. Вы скорее говорите о «handler'е» («обработчике»).
Опечатался. «Cather» читать как «catcher».
И да и нет.

Я себе представлял catchException как шаблонный метод. Он инкапсулирует только «что нужно сделать», но не «как это делается». Я, видимо, зря не написал этого в статье.
То, что метод catchException инкапсулирует «что нужно сделать» я понял. Я говорю, что сам класс исключения в такой ситуации выполняет две обязанности, что не очень хорошо.
Кстати, такое применение исключений опасно ещё по одной причине. Очень непривычно их использование. Насколько вероятно, что новый разработчик в команде будет и использовать и реализовывать исключения как надо? Мне кажется, новичок легко и непринуждённо, без задней мысли напишет try-catch в обычном стиле.
Возможно. Этот метод, вообще гововоря, довольно узкого профиля и если воткнуть его «не туда», то проблем не оберёшься. В этом я с выми полностью согласен.

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

Метод, конечно, сырой. Надо будет ещё раз пересмотреть плюсы и минусы.
Рад, что Вы так восприняли критику. Уверен, Вы неспроста пришли к такой методике, а на то были свои причины. Если решитесь применять подход, буду рад ознакомиться с плюсами и минусами этой практики из жизни. И да, спасибо за статью, «разрыв шаблона» понравился.
Конструктивная критика — это всегда очень ценно. Я, в общем-то, за ней и пришел.
Обработка исключений не может быть универсальной по своей сути.

Это может быть логгирование (тут, наверное, единственное место универсальное) может быть повтор метода (без вариантов переноса кода), может быть какая-то логика (и тут круто увеличится зависимость одного класса от другого), может быть сложная логика с делегированием части обработки другому классу (программист который передаст, например, ну не знаю, репозиторий базы конструктор исключения должен быть жестоко наказан).

Из-за неуниверсальности обработки код получится сложнее: тут исключение само себя обрабатывает (чтобы узнать как читайти внутри), тут нет. Представьте себя читающим свой же код через 3 года. Хренпоймёшь и без этого, сложности и без этого достаточно. А тут ещё эта дополнительная сложность на пустом месте.
Про Single Responsibility сверху уже отметили.
Проблема в том, что только код, который вызывает метод, бросающий исключения, должен знать как его обработать. Иначе вы пытаетесь предусмотреть все варианты вызова вашего метода.
А мне понравилось.

1. «Сразу скажу, что для меня исключение != ошибка...». Я с Вами.
2. Мне пративно, когда вижу три и более catch-конструкции. А здесь увидел простое и изящное средство от своих комплексов.
3. Помоему есть некоторые проблеммы гибкости с типами исключений и перехватом (возможно я и ошибаюсь — будет ястно когда воспользуюсь советом). На 100% они решаемы.

Аналогичным образом я передаю часть ответственности классам аттрибутов. Может поэтому мне не кажется это нарушением SOLID-принципов.
1. Про «исключение != ошибка» и я с вами обоими. :)
2. А вот три вложенных try-catch намекают на потребность в вынесении методов в большинстве случаев.
К сожелению «выделение метода» catch-тел не уменьшает количества catch-блоков.

Если логика разная, то это приведет к появлению нескольких функций в носителе метода с try...catch. Из статьи я вынес возможность перености эти методы в соответствующие exception-классы. И вызывать их через полиморфизм.
> К сожелению «выделение метода» catch-тел не уменьшает количества catch-блоков.

Ну, тут вариантов два. Либо несколько try-catch блоков, разбросанных по «вложенным методам» это норма для конкретного решения. Либо присутствует какая-то ошибка проектирования. Сами по себе многие try-catch блоки, разбросанные по методам это абсолютно не проблема, если эти блоки необходимы для корректной, ожидаемой, предсказуемой работы метода. Это, кстати, идея не моя, взята из «clean code».

А мне, кстати, понравилась нестандартность решения, описанного в статье.
Я понял, что вам тоже понравилось. За предыдущее высказывание хотел поставить плюс. Но у меня принцип — не оценивать непосредственного собеседника :). Разговаривая с вами, стараюсь понять все грани обсуждаемой идеи.

Несколько try..catch блоков. Получается что-то типа цепочки обязаностей. Вариант. Но по архитектурной сложности это почти тоже самое (по одному методу на catch). Вопрос стоит только в расположении методов.

Я понимаю, что try..catch..catch… — в конкретном месте необходим. Но ощущение дублирования от этого не уменьшается. Понимание необходимости притупляет желание исправить, но код не улучшает.

Кстати. Больше половины моих исключений выглядит так:

class MyException: Exception {}


До меня сейчас дошло — он же ленив. Вот, что значит новый взгляд на знакомые вещи.
Честно говоря, я к исключениям отношусь довольно просто. Исключение это просто ситуация, которую сам метод, возбудающий исключение, не обрабатывает сам, т.к. он не знает как, да и вообще это не его ответственность. Ответственность за обработку ложится на пользователя метода, т.к. «ему виднее». Учитывая это, можно ещё раз рассмотреть вложенные методы с исключениями. Получается, что каждый метод снимает с себя ответственность за одни ситуации и передаёт обработку наверх, там в обработке могут возникнуть свои проблемы. Я считаю, это абсолютно нормально.

Намного более насторожил меня такой код, в котором блоки try вставляются в catch-блоки по нескольку раз. Тут уже стоит задуматься о дизайне.

Что касается простых классов исключений, вида class MyException: Exception {}, то это тоже хорошо. Наследование классов исключений — занятие чреватое. В мире .NET об угрозах такого кодирования написано в «CLR via C#». В общем, широкий набор классов исключений это хорошо. Главная причина в том, что catch (MyConcreteException ex) будет известно что обрабатывать. А в случае наличия наследников, всё будет сильно сложнее. Например, может потребоваться писать мерзкие проверки вида if (ex is MyConcreteSpecificException)
Прикольно. С помощью полиморфизма мы решаем проблему «мерзких проверок».

Говоря о ленивости я имел введу отсутствие членов у класса исключения. А значит он имеет хороший потенциал по их принятию (в результате «переноса метода») от других классов.
Я услышал Вас. Но как-то больше доверяю классическому подходу, когда исключение привычно описывает ситуацию, а клиент метода обрабатывает его. Проще поддерживать, проще понимать.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории