Как проектировать, тестировать и доказывать работоспособность программы?
Разработка начинается с постановки задачи и модели данных, продолжается декомпозицией, реализацией и проверкой, а не сводится к набору операторов. Официальная программа МФТИ включает структурное проектирование, подпрограммы, библиотеки, объектные понятия, трассировку и доказательство правильности.
Декомпозиция сверху вниз
Сначала формулируют крупные обязанности, затем уточняют каждую до функций с понятными контрактами. Нижний уровень скрывает детали: чтение формата, вычисление, представление результата. Это уменьшает число состояний, которые приходится удерживать одновременно при анализе.
Сборка снизу вверх
Иногда сначала создают надёжные примитивы и объединяют их в более крупный модуль. Подход полезен для библиотек и повторно используемых структур. В реальном проекте оба направления встречаются вместе: архитектура задаёт границы сверху, проверенные компоненты поддерживают сборку снизу.
Тестовые классы
Хороший набор включает обычный случай, минимальные и максимальные размеры, пустые данные, повторения и значения рядом с границей ветвления. Тест должен иметь ожидаемый результат, полученный независимо от проверяемого кода. Случайные тесты дополняют, но не заменяют целевые контрпримеры.
Трассировка и доказательство
Таблица трассировки показывает конкретные значения переменных по шагам и помогает найти дефект. Инвариант и контракт доказывают свойство для класса входов. Успешная трасса одного примера не равна доказательству, а доказательство модели не исключает ошибку перевода в код — нужны оба уровня.
Объектные границы
Инкапсуляция прячет допустимое состояние за операциями, наследование выражает отношение специализации, полиморфизм позволяет работать через общий контракт. Эти средства применяют только когда они упрощают модель. Для короткой вычислительной задачи несколько чистых функций часто яснее искусственной иерархии классов.
Тесты выводятся из разбиения входного пространства
Для функции нормализации оценки естественны классы: значение ниже диапазона, внутри и выше. Проверка только среднего значения не касается ветвей. Табличный тест хранит вход и ожидаемый результат рядом и облегчает добавление границ.
def clamp(value: int, minimum: int, maximum: int) -> int:
if minimum > maximum:
raise ValueError("invalid interval")
return max(minimum, min(value, maximum))
cases = [(-1, 0), (0, 0), (5, 5), (10, 10), (11, 10)]
for value, expected in cases:
assert clamp(value, 0, 10) == expectedТесты подтверждают примеры, а доказательство объясняет все входы: min ограничивает сверху, max — снизу, и внутри интервала значение не меняется. Проверка ошибочного интервала относится к предусловию.
Практика: от спецификации до набора регрессий
Спроектируйте функцию подсчёта стоимости доставки с тремя весовыми диапазонами и запретом отрицательного веса. Сначала создайте таблицу решений, затем выделите значения непосредственно до, на и после каждой границы. Реализуйте чистое вычислительное ядро отдельно от ввода и вывода. Намеренно внесите две ошибки — неверный знак сравнения и перестановку ветвей — и убедитесь, что тесты их ловят. Для найденного дефекта оставьте минимальный регрессионный тест.
Источники
- МФТИ: Программа вступительного испытания по информатике и информационно-коммуникационным технологиям.
- Информатика и ИКТ. Практикум по программированию. 10–11 классы. Базовый уровень / под ред. Макаровой Н.В. — СПб.: Питер, 2015.
- Астафьева Н.Е. Информатика и ИКТ: практикум для профессий и специальностей технического и социально-экономического профилей. — М.: Academia, 2019. — 384 с.