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 чем Pазрешение):

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 ответов


лично, и я думаю, что это подкреплено конвенцией, 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.

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

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

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

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


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


исключения обрабатываются более эффективно в 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

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

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

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

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

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