Конструирование программного обеспечения

Тема 4

Созданный разработчиками технический проект, написанный на различных языках программирования – составляет код. Код программного обеспечения должен быть понятным и легко читаемым, а также обеспечивать заданную в требованиях работу программы. И его создание требует выполнения отладки, проверки, комментирования и множества других операций

Конструирование – детальное создание рабочей программной системы посредством комбинации кодирования, верификации (проверки), модульного тестирования (unit testing), интеграционного тестирования и отладки.

Артефакты: Программный код, тестовые наборы

1. Основные принципы конструирования

Четыре фундаментальных принципа конструирования, которые каждый разработчик должен применять на практике.

1.1 Минимизация сложности

Главный враг программной инженерии — сложность. Человеческий мозг не способен удерживать в памяти устройство всей системы сразу (ограничение рабочей памяти).

  • Как реализуется:
    • Писать код для людей, а не для компьютера (читаемость важнее микрооптимизаций).
    • Использование единых стандартов кодирования (Code Style).
    • Создание коротких методов (до 20–30 строк) с низким уровнем цикломатической сложности.
    • Отказ от «умных» запутанных трюков в пользу простых, очевидных решений (принцип KISS — Keep It Simple, Stupid).

1.2 Ожидание изменений

Программное обеспечение эволюционирует на протяжении всего жизненного цикла. Код, написанный сегодня, завтра будет изменен из-за новых требований бизнеса.

  • Как реализуется:
    • Использование полиморфизма и интерфейсов вместо жесткой привязки к конкретным классам.
    • Вынесение бизнес-правил, которые часто меняются, в файлы конфигурации, БД или переменные окружения (.env).
    • Изоляция сторонних библиотек за собственными обертками (Паттерн «Адаптер»), чтобы их замена не ломала код всего приложения.

1.3 Защитное программирование

Защитное программирование (Defensive Programming) исходит из постулата: «Все внешние данные враждебны, а в коде коллег (и своем собственном) обязательно есть ошибки». Цель — не допустить падения программы при некорректных действиях.

  • Как реализуется:
    • Проверка всех входящих аргументов, параметров API, данных из форм на null, пустые строки, некорректные типы и диапазоны.
    • Использование блоков try-catch, но без «проглатывания» исключений (пустой catch — грубейшая ошибка).
    • Использование утверждений для проверки инвариантов кода во время разработки (например, assert(age > 0)).

1.4 Повторное использование

Разработка «с нуля» экономически невыгодна и повышает вероятность ошибок. Повторное использование (Reuse) бывает двух видов:

  • Внутреннее, через выделение дублирующегося кода внутри проекта в переиспользуемые функции, базовые классы или локальные библиотеки (принцип DRY — Don’t Repeat Yourself).
  • Внешнее, через интеграцию проверенных временем готовых решений — open-source библиотек (через npm, NuGet, Maven), фреймворков и облачных сервисов.

2. Техники реализации

Проектирование по контракту

Контракт превращает неявные ожидания между вызывающим и вызываемым кодом в проверяемые условия.

SC_contract.png

Инженерная задача

Для заданного метода определить:

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

Артефакт

Спецификация метода

Обработка ошибок и исключений

Исключение — это механизм передачи информации об ошибочной ситуации, а не способ скрыть ошибку.

exception.png

Инженерная задача

Для заданного сценария определить стратегию обработки ошибки.

Артефакт

Политика обработки ошибок

Управление зависимостями

Чем сильнее код зависит от конкретной реализации, тем дороже изменение этой реализации.

Инженерная задача

Найти зависимости:

Class A → Class B → Database

и определить, какие из них следует инвертировать через интерфейс.

Артефакт

Dependency Graph / Interface

3. Отладка и техники статического анализа кода

Обеспечение качества кода в процессе конструирования делится на динамические методы (выполнение кода) и статические (анализ текста кода).

Статический анализ кода

Проверка исходного кода без его запуска. Позволяет найти до 60% глупых ошибок еще до компиляции.

  • Линтеры (Linters) позволяют проводить проверку синтаксиса и форматирования кода (например, ESLint, SonarQube).
  • Поиск уязвимостей (SAST) через сканирование кода и проблем в безопасности (SQL-инъекции, пароли).
  • Инспекции и Ревью (Code Review) - анализ кода другими разработчиками (Peer Review) для обмена опытом и проверки архитектурного соответствия.

Инженерная задача

Дан исходный код.

Провести:

  1. автоматический анализ;
  2. code review;
  3. классификацию найденных проблем.

Артефакт

Отчет об анализе кода

Отладка

Процесс локализации, поиска причины и исправления уже обнаруженной ошибки.

  • Инструменты отладки:
    • Интерактивные отладчики среды разработки осуществляют выполнение кода по шагам (Step Over/Into), расстановка точек останова (Breakpoints), инспекция текущего состояния переменных (Watch).
    • Логирование (Logging) путем записи ключевых событий работы программы в файл (логи). Незаменимо для отладки на «живых» серверах, где нельзя поставить точку останова и остановить систему.

4. Интеграция

сборка отдельных сконструированных модулей и компонентов в единую, целостную систему.

integration.png

**Стратегии интеграции **

  1. Нисходящая - с верхних уровней архитектуры (интерфейс, координаторы). Вместо еще не написанных нижних модулей (БД, расчеты) используются временные заглушки (Stubs).

  2. Восходящая - с базовых низкоуровневых модулей (драйверы, утилиты). Для их тестирования пишутся специальные вызывающие модули-драйверы (Drivers).

  3. Большой взрыв (Big Bang) - все модули пишутся отдельно, а затем соединяются в один.

  4. Непрерывная интеграция (Continuous Integration, CI), когда код собирается несколько раз в день в общий репозиторий. На каждый коммит автоматически запускается сервер сборки, который компилирует проект, прогоняет линтеры, статический анализ и автоматические Unit-тесты.

Инженерная задача

Дана система и порядок готовности компонентов.

Выбрать стратегию интеграции и определить:

  • какие модули интегрировать первыми;
  • где нужны “заглушки”;
  • где нужны “драйверы”;
  • где запускать автоматические тесты.

Артефакт

Интеграционный план

📝 Стандартные задачи