Декомпозиция программы на функции
Содержание курса
Пошаговая процедура разбиения программы на функции
Теперь посмотрим, как всё это работает на практике — от монолитного кода до готового набора функций.
Возьмём конкретную программу: она запрашивает цену товара, применяет скидку 10% и печатает итоговую сумму. В исходном виде всё написано подряд, одним блоком:
price = float(input("Введите цену: "))
discounted = price * 0.9
print(f"Итого со скидкой: {discounted:.2f} руб.")
Пока три строки — всё читается. Но представьте, что таких расчётов пять, и каждый немного отличается. Вот тут начинается боль. Разберём декомпозицию по шагам.
Шаг 1. Найти логически самостоятельные блоки
Смотрим на код и задаём себе вопрос: что здесь происходит? Три отдельных действия:
- получить цену от пользователя;
- посчитать цену со скидкой;
- показать результат.
Каждое из них — кандидат на отдельную функцию.
Шаг 2. Придумать говорящие имена
По одному глаголу на каждое действие: get_price, apply_discount, print_receipt. Если имя не придумывается без союза «и» — блок нужно делить дальше.
Шаг 3. Оформить каждый блок как функцию
Жёстко заданные значения (например, коэффициент скидки 0.9) выносим в параметры — так функция становится гибкой:
def get_price():
return float(input("Введите цену: "))
def apply_discount(price, rate):
return price * (1 - rate)
def print_receipt(price):
print(f"Итого со скидкой: {price:.2f} руб.")
Обратите внимание: apply_discount не знает, откуда пришла цена и куда пойдёт результат. Она только считает. Именно это делает её универсальной.
Шаг 4. Заменить исходный код вызовами функций
price = get_price()
discounted = apply_discount(price, rate=0.1)
print_receipt(discounted)
Три строки остались тремя строками, но теперь каждая из них — осмысленное действие с именем, а не безымянное вычисление.
Шаг 5. Запустить программу и сравнить результат
Запускаем, вводим то же значение, что вводили раньше, и проверяем: вывод совпадает с исходным? Если да — рефакторинг прошёл чисто. Программа делает то же самое, просто теперь это видно по именам функций, а не по деталям арифметики.
Вот промежуточный результат — функции выделены, вызовы на верхнем уровне:
def get_price():
return float(input("Введите цену: "))
def apply_discount(price, rate):
return price * (1 - rate)
def print_receipt(price):
print(f"Итого со скидкой: {price:.2f} руб.")
price = get_price()
discounted = apply_discount(price, rate=0.1)
print_receipt(discounted)
Если через месяц понадобится изменить ставку скидки или поменять формат вывода — вы точно знаете, в какой функции это сделать. И только в ней.
Но у этого варианта есть один недостаток: непонятно, где именно «начинается» программа — определения функций и вызовы перемешаны на верхнем уровне. В следующем разделе мы сделаем ещё один шаг — явно обозначим точку входа через main().
