Задать вопрос

Можно ли выбрасывать исключение в предикатах?

Доброго дня.
Вопрос касается чистоты кода и стройности архитектуры. Не секрет, что предикаты - функции, которые должны возвращать булево значение, отвечая на какой-либо вопрос (is directory exists?). Поскольку я год назад начал плотно угорать по исключениям (ибо они рулят), ныне я редко возвращаю false или null в случаях, когда false или null должны вести за собой прекращение работы программы. Я продолжаю их возвращать, когда ситуация допустима, но когда это ошибка - всегда выбрасываю исключение.

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

Закончу вопрос примером.
<?php

class CheckForNewPhoto
{
    public function handle(Repositories\MosaicRepository $MosaicRepository)
    {
        if($this->isDirectoryNameCorrect()) {
            // ранее исключение выбрасывалось тут, я считаю, это некрасиво
            // do stuff
        }

        throw new Exceptions\UnexpectedError(
            'Something wrong here'
        );
    }

    /**
     * Вот это - предикат. Я сделал так, чтобы избавить entry point,
     * функцию handle, от ада выбросов исключений, когда проверяется много
     * условий и выкидывается много исключений в одном методе.
     * Когда один метод > одна проверка > строго связанные с ней исключения,
     * это красиво.
     */
    private function isDirectoryNameCorrect()
    {
        if(!config('mosaic.directory', null)) {
            throw new Exceptions\InvalidSettingsException(
                'Directory for mosaic photos is not setup properly'
            );
        }

        return true;
    }
}

Заранее спасибо.
  • Вопрос задан
  • 486 просмотров
Подписаться 4 Средний 7 комментариев
Решения вопроса 3
inoise
@inoise
Solution Architect, AWS Certified, Serverless
Держащий в руке молоток видит только гвозди


Это про вас. Предикат обязан возвращать true/false. Эксепшены нужны для того чтобы отлавливать ошибки и передавать из вверх по стеку.

Пример из жизни: на вопрос есть ли пирожки продавец или говорит да или разворачивается и уходит
Ответ написан
angrySCV
@angrySCV
machine learning, programming, startuping
Предикаты возвращают логическое выражение и точка. Если он возвращает что-то еще, то это уже не предикат.
Многие методы например работают с предикатами, и они не будут не корректно работать с функцией которая выбрасывает исключения. Поэтому тебе лучше скомбинировать/объеденить несколько нужных функций в твоем кейсе, а не пытаться создавать одну под все задачи.
Ответ написан
Комментировать
@vanyamba-electronics
Приведу пример:
void checkDirectory( const Dir& dir ) {
    if (isDirBad( dir ))
       throw "The directory is bad bad directory";
}

void File::open( const FileName& fn ) {
    try {
       checkDirectory( Dir( fn ));
       openFile(fn);
    }
    catch( const char* msg ) {
        std::cerr << msg << std::endl;
    }
}

Логично, что предикат `isDirBad()` не должен вызывать исключение, потому что это будет масло масленное.
Скажем так - хороший дизайн состоит в том, что исключения генерит программа пользователя, а библиотека работает с предикатами. В этом случае библиотеку будет легко портировать для использования в других языках программирования.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 5
BorLaze
@BorLaze
Java developer
Пример как раз отлично иллюстрирует то, что исключений тут и даром не надо.

На вопрос isDirectoryNameCorrect мы хотим получить только "да" или "нет".
При этом нас совершенно не беспокоит, почему "нет" - то ли набор символов в имени недопустим, то ли диск посыпался, то ли прав не хватает - нам это не интересно.
Ответ написан
Комментировать
@rPman
Проблема больше не когда исключение бросать, а когда ловить! В конкретно вашем примере вы должны будете поймать исключение, завершить операцию работы с файлами и продолжить выполнение.

Неужели вы хотите ронять приложение, если пользователь выбрал в настройках не ту директорию? Например была выбрана флешка а потом ее извлекли, ваша программа попытается открыть катало и упадет.
Ответ написан
Комментировать
@nrgian
Исключения - это хорошее название. Точное. Удачное.
Они должны использоваться в непредусматриваемых ситуациях.

Описанная вами ситуация - штатная.
Тут правильно возвращать истину или ложь.
Ответ написан
Комментировать
longclaps
@longclaps
Исключения - это плохое название. Запутывающее. Неудачное.
На самом деле за этим скрывается альтернативный поток управления: есть обычный ход выполнения программы, а есть способ телепортироваться из точки возбуждения исключения в точку его обработки. И это бывает очень удобно: например, ищешь чего-нибудь рекурсивным обходом по графу, нашел - кинул исключение, а подхватил его обработчиком вне функции обхода. Это очень удобно - не надо при выходе из функции возвращать true/false, а в точке вызова его проверять, получается просто и элегантно.
Ответ написан
ApeCoder
@ApeCoder
Рассматривать ситуацию как исключительную или нет - дело дизайна. Вы можете либо сделать предикат, но тогда эта штука долга возвращать true или false и выкидывать исключения других ситуациях - например при нехватке памяти.

Либо сделать проверку и выкидывать исключение, но тогда незачем возвращать true - это будет уже не предикат.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы