Если вам нужно более 3 уровней отступов, вы облажались!
Если вам нужно более 3 уровней отступов, вы все равно облажались и должны исправить свою программу. Вы можете найти эту строку в официальном стиле кодирования ядра Linux Линуса Торвальдса.
Работать с глубоко вложенным кодом — все равно что пытаться распутать бесконечный набор матрешек — это бесконечная задача. Как разработчики программного обеспечения, мы обязаны писать код, который легко читается нашими товарищами по команде. Но иногда мы сталкиваемся с кодом, который настолько глубоко вложен, что кажется монстром, которого нужно победить. Именно тогда пришло время засучить рукава и убить демона глубокой вложенности путем рефакторинга кода. Поверьте, ваши коллеги будут вам благодарны за облегчение их жизни.
Как? Я расскажу о двух методах рефакторинга глубоко вложенного кода;
- Извлечение
- Инверсия
Добыча
Глубоко вложенный код часто может быть признаком того, что блок кода нарушает принцип 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/