LBYL против EAFP в Java?


недавно я учился на Python и обнаружил идиомы LBYL/EAFP в отношении проверки ошибок Перед выполнением кода. В Python, похоже, принятый стиль-EAFP, и он, похоже, хорошо работает с языком.

LBYL (LООК Bдо You Leap):

def safe_divide_1(x, y):
    if y == 0:
        print "Divide-by-0 attempt detected"
        return None
    else:
        return x/y

EAFP (это Easier to A sk Forgiveness чем Permission):

def safe_divide_2(x, y):
    try:
        return x/y
    except ZeroDivisionError:  
        print "Divide-by-0 attempt detected"
        return None

мой вопрос таков: Я никогда даже не слышал об использовании EAFP в качестве основной конструкции проверки данных, исходя из фона Java и C++. Является ли EAFP чем-то, что разумно использовать в Java? Или слишком много накладных расходов от исключений? Я знаю, что есть только накладные расходы, когда на самом деле возникает исключение, поэтому я не уверен, почему не используется более простой метод EAFP. Это просто предпочтение?

5   51  

5 ответов:

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

if (o != null)
    o.doSomething();
else
    // handle

против:

try {
    o.doSomething()
}
catch (NullPointerException npe) { 
    // handle
}

кроме того, рассмотрим следующее:

if (a != null)
    if (b != null)
        if (c != null)
            a.getB().getC().doSomething();
        else
            // handle c null
    else
        // handle b null
else
    // handle a null

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

как я вижу, EAFP никогда не следует использовать, за исключением редких ситуаций. Кроме того, поскольку вы подняли этот вопрос: да, блок try-catch действительно несет некоторые накладные расходы даже если исключение не брошено.

Если вы обращаетесь к файлам, EAFP более надежен, чем LBYL, потому что операции, связанные с LBYL, не являются атомарными, и файловая система может меняться между временем, когда вы смотрите, и временем, когда вы прыгаете. На самом деле, стандартное имя TOCTOU - время проверки, Время использования; ошибки, вызванные неточной проверкой, являются ошибками TOCTOU.

рассмотрите возможность создания временного файла, который должен иметь уникальное имя. Лучший способ узнать, существует ли выбранное имя файла еще-это попробовать создать it-убедитесь, что вы используете параметры для обеспечения сбоя операции, если файл уже существует (в терминах POSIX / Unix флаг O_EXCL равен open()). Если вы попытаетесь проверить, существует ли файл (возможно, используя access()), то между временем, когда это говорит "нет" и время, когда вы пытаетесь создать файл, кто-то или что-то еще, возможно, создал файл.

и наоборот, предположим, что вы пытаетесь прочитать существующий файл. Ваша проверка того, что файл существует (LBYL) может сказать: "это там", но когда вы на самом деле открываете его, вы находите"его там нет".

в обоих этих случаях вы должны проверить окончательную операцию-и LBYL автоматически не помог.

(Если вы возитесь с программами SUID или SGID,access() задает другой вопрос; это может иметь отношение к LBYL, но код все равно должен учитывать возможность сбоя.)

в дополнение к относительной стоимости исключений в Python и Java, имейте в виду, что есть разница в философии / отношении между ними. Java пытается быть очень строгим в отношении типов (и всего остального), требуя явных, подробных объявлений сигнатур классов/методов. Он предполагает, что вы должны в любой момент точно знать, какой тип объекта вы используете и что он способен делать. Напротив, "утиный ввод" Python означает, что вы не знаете наверняка (и не должны care) что такое тип манифеста объекта, вам нужно только заботиться о том, что он крякает, когда вы его просите. В такого рода питательную среду, единственный вменяемый отношение предполагать, что все получится, но будьте готовы иметь дело с последствиями, если они не. Природные Ява ограничения не сочетается с такой случайный подход. (Это не имеет целью унизить ни подход, ни язык, а скорее сказать, что эти отношения являются частью идиомы каждого языка и копируют идиомы между разными языками часто может возникнуть неловкость и плохое общение...)

исключения обрабатываются более эффективно в Python, чем в Java, что по крайней мере частично почему вы видите эту конструкцию в Python. В Java более неэффективно (с точки зрения производительности) использовать исключения таким образом.

рассмотрим эти фрагменты кода:

def int_or_default(x, default=0):
    if x.isdigit():
        return int(x)
    else:
        return default

def int_or_default(x, default=0):
    try:
        return int(x)
    except ValueError:
        return default

они оба выглядят правильно? Но один из них-нет.

первый, используя LBYL, терпит неудачу из-за тонкого различия между isdigit и isdecimal; при вызове со строкой "①23₅", он будет выдавать ошибку, а не правильно возвращать значение по умолчанию.

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

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

стоит отметить, что EAFTP не об исключениях, и Java-код особенно не должен использовать исключения повсеместно. Речь идет о предоставление правильного задания правому блоку кода. В качестве примера, используя Optional возвращаемые значения-это совершенно правильный способ написания кода EAFTP и гораздо более эффективен для обеспечения правильности, чем LBYL.