Если вам нужно более 3 уровней отступов, вы облажались!

Если вам нужно более 3 уровней отступов, вы все равно облажались и должны исправить свою программу. Вы можете найти эту строку в официальном стиле кодирования ядра Linux Линуса Торвальдса.

Работать с глубоко вложенным кодом — все равно что пытаться распутать бесконечный набор матрешек — это бесконечная задача. Как разработчики программного обеспечения, мы обязаны писать код, который легко читается нашими товарищами по команде. Но иногда мы сталкиваемся с кодом, который настолько глубоко вложен, что кажется монстром, которого нужно победить. Именно тогда пришло время засучить рукава и убить демона глубокой вложенности путем рефакторинга кода. Поверьте, ваши коллеги будут вам благодарны за облегчение их жизни.

Как? Я расскажу о двух методах рефакторинга глубоко вложенного кода;

  1. Извлечение
  2. Инверсия

Добыча

Глубоко вложенный код часто может быть признаком того, что блок кода нарушает принцип Single Responsibility из SOLID — он делает слишком много вещей, и сложно понять, что происходит. . В этих случаях может быть полезно извлечь блоки кода и превратить их в собственные функции. Это не только сделает ваш код более организованным и читабельным, но также облегчит его повторное использование и поддержку в долгосрочной перспективе.

Давайте рассмотрим пример извлечения:

#FIXME: extract the process of finding sum
def compute(x: int, y: int) -> int:
    if x > y:
        sum = 0
        for num in range(y, x + 1):
            sum += num
        return sum
    else:
        return 0
def process(x: int, y: int) -> int:
    sum = 0
    for num in range(y, x + 1):
        sum += num
    return sum


def compute(x: int, y: int) -> int:
    if x > y:
        return process(x, y) #extracted function
    else:
        return 0

Инверсия

Когда мы программируем, легко попасть в ловушку менталитета «счастливого пути» — мы сосредотачиваемся на успешном результате и соответствующим образом структурируем нашу логику. Но это может привести к глубоко вложенным блокам кода, которые трудно читать и понимать. Вот тут-то и появляется шаблон Guard Clause (также известный как шаблон Bouncer).

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

Давайте рассмотрим пример инверсии кода:

#FIXME: invert and add a guard clause
def compute(x: int, y: int) -> int:
    if x > y:
        sum = 0
        for num in range(y, x + 1):
            sum += num
        return sum
    else:
        return 0
def compute(x: int, y: int) -> int:
    if x <= y: #guard clause
        return 0

    sum = 0
    for num in range(y, x + 1):
        sum += num
    return sum

Теперь давайте посмотрим, что произойдет, если мы выполним и извлечение, и инверсию.

def process(x: int, y: int) -> int:
    sum = 0
    for num in range(y, x + 1):
        sum += num
    return sum


def compute(x: int, y: int) -> int:
    if x <= y:
        return 0

    return process(x, y)

Разве это не выглядит лучше? Процесс рефакторинга улучшил читаемость кода и уменьшил цикломатическую сложность.

Другой пример

Давайте рассмотрим еще один пример глубоко вложенного кода и проведем его рефакторинг с использованием вышеупомянутых процедур. Я выбрал UVa-10070 для иллюстрации примера, который является довольно простой задачей из UVa Online Judge.

Проблема

10070 Високосный год или не високосный год и…

Древняя раса Гуламату очень продвинута в своей схеме исчисления года. Они понимают, что такое високосный год (год, который делится на 4 и не делится на 100, за исключением того, что годы, которые делятся на 400, также являются високосными), и у них также есть похожие праздничные годы. Одним из них является фестиваль Хулукулу (проходит в годы, кратные 15) и фестиваль Булукулу (проходит в годы, кратные 55, при условии, что это также високосный год). Учитывая год, вы должны будете указать, какими свойствами обладают эти годы. Если год не високосный и не праздничный, то выведите строку «Это обычный год». Порядок вывода свойств (если они есть): високосный год → хулукулу → булукулу.

Пример ввода

2000
3600
4515
2001

Пример вывода

This is leap year.

This is leap year.
This is huluculu festival year.

This is huluculu festival year.

This is an ordinary year.

Решение №1 [глубоко вложенный код]

import sys

if __name__ == "__main__":

    first_input = True

    for line in sys.stdin:
        if not first_input: #FIXME
            print()
        else:
            first_input = False

        input_year = int(line)

        is_leap_year = False
        is_huluculu_year = False
        is_bulukulu_year = False

        if input_year % 4 == 0: #FIXME
            if input_year % 100 == 0:
                if input_year % 400 == 0:
                    is_leap_year = True

                    if input_year % 55 == 0: #FIXME
                        is_bulukulu_year = True
            else:
                is_leap_year = True

                if input_year % 55 == 0: #FIXME
                    is_bulukulu_year = True

        if input_year % 15 == 0: #FIXME
            is_huluculu_year = True

        if is_leap_year:
            print("This is leap year.")
        if is_huluculu_year:
            print("This is huluculu festival year.")
        if is_bulukulu_year:
            print("This is bulukulu festival year.")

        if not is_leap_year and not is_huluculu_year and not is_bulukulu_year:
            print("This is an ordinary year.")

Приведенный выше код был намеренно написан с опасной глубокой вложенностью.

Решение №2 [Рефакторинг]

import sys

FLAG_FIRST_INPUT = True


def print_blank_line_if_not_first_input():
    global FLAG_FIRST_INPUT

    if FLAG_FIRST_INPUT:
        FLAG_FIRST_INPUT = False
        return

    print()


def check_leap_year(year: int) -> bool:
    if year % 400 == 0:
        return True
    if year % 100 == 0:
        return False
    if year % 4 == 0:
        return True

    return False


def check_huluculu_year(year: int) -> bool:
    if year % 15 == 0:
        return True

    return False


def check_bulukulu_year(is_leap_year: bool, year: int) -> bool:
    if is_leap_year and year % 55 == 0:
        return True

    return False


def process_input(year: int):
    is_leap_year = check_leap_year(year)
    is_huluculu_year = check_huluculu_year(year)
    is_bulukulu_year = check_bulukulu_year(is_leap_year, year)

    if is_leap_year:
        print("This is leap year.")
    if is_huluculu_year:
        print("This is huluculu festival year.")
    if is_bulukulu_year:
        print("This is bulukulu festival year.")

    if not is_leap_year and not is_huluculu_year and not is_bulukulu_year:
        print("This is an ordinary year.")


if __name__ == "__main__":

    for line in sys.stdin:
        print_blank_line_if_not_first_input()
        process_input(int(line))

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

Want to Connect?
If you have any feedback, please ping me on my 

LinkedIn: https://linkedin.com/in/shuhanmirza/