ONE DUDE`S BLOG

Чем плох оператор else

30.08.2020
Какую опасность несет оператор else? Почему им плохо злоупотреблять и в большинстве случаев лучше его избегать
Это серия статей

В предыдущей статье мы говорили про оператор switch/case, думаю с этим, более менее, понятно. Менее очевидный оператор else. Пожалуй, в некоторых случаях он оправдан, но чаще всего, лучшим решением будет избежать использование этого оператора.

Проблема

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


def check_violation(car)
        if car.type != 'military' and speed > 120:
                driver = get_driver(car):
                if driver.is_officials():
                        skip_car()
                else:
                        fine()
        else:
                skip_car(car)

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

Решение

Как можно избежать использование else в большинстве случаев? На самом деле достаточно просто, и это точно не займет у вас много времени.

Вынесем логику проверки условий остановки машины в отдельные методы

def should_stopped(car, speed):
        driver = get_driver(car)
        return speed > 120 and car.type != 'military' and not driver.is_officials()

def check_car(car):
        if should_stopped(car):
                fine()
                return
        skip_car(car)        

Разумеется в реальном мире мы бы вынесли проверку нарушений ПДД в отдельную абстракцию, но в данном случае, я решил опустить этот момент.

Хочу отметить что в функции check_car мы не использовали else, вместо этого мы использовали return из первого условия, такой подход избавляет нас от еще 1 потенциального ветвления и вложенности кода. Однако, в некоторых ситуациях, это может привести к весьма избыточному коду, например после завершения основной логики мы захотим закрыть коннект к базе данных, в этом случае, нам бы пришлось писать закрытие соединение перед каждым return. Однако если у вас возникает такая необходимость, то скорее всего, в коде нарушено разделение на слои, мы можем закрыть соединение с базой данных на уровне выше, после того как функция проверки завершится.

Также я рекомендую (однако многие с этим не согласятся), использовать инверсную логику, например код наподобие этого

def handle_rspns(rspns):
        if rspns.status_code == 200:
                do_something(rspns.data)

Я бы предложил заменить на это:

def handle_rspns(rspns):
        if rspns.status_code != 200:
                return
        do_something(rspns.data)

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

Чистый код
1
705