Проектирование программы
Нисходящая разработка.
Нисходящую разработку можно рассматривать как процесс, состоящий из трех этапов:
- проектирование;
- планирование;
- реализация.
Законы Мэрфи.
Все оказывается сложнее, чем кажется.
Все тянется дольше, чем можно ожидать.
Все оказывается дороже, чем планировалось.
Если что-то может испортиться, оно обязательно испортится.
Комментарий Каллагена: «Мэрфи был оптимистом».
Эти законы хорошо описывают проблемы традиционной восходящей технологии программирования.
Цели нисходящей разработки:
- уменьшить сложность программ;
- уменьшить время разработки;
- позволить как можно раньше обнаружить ошибки.
Одним из принципов проектирования программ является модульность программ. Модуль – это программа, которую можно вызвать из любого другого модуля и можно отдельно откомпилировать.
Разбиение программ на модули характеризуется следующими преимуществами:
- сокращение сроков написания программы, так как проектирование модулей можно поручить разным программистам;
- можно создавать библиотеки наиболее часто употребляемых функций;
- упрощается загрузка больших программ в ОЗУ – не требуется сегментация всей программы, так как это достигается естественным образом при разбиении на модули;
- для руководства легче наблюдать за продвижением проекта;
- облегчается тестирование;
- проще проектирование и последующие изменения программы.
Наряду с этим имеются и недостатки:
- может увеличиться время выполнения программы;
- может возрасти размер памяти, требуемый программе;
- может увеличиться время компиляции и загрузки;
- при некорректном разбиении программ на модули могут возникнуть проблемы межмодульного взаимодействия (особенно при внесении изменений).
-
Для современной техники эти недостатки несущественны и окупаются за счет сокращения сроков разработки и сопровождения.
Разбиение программы на модули нужно производить по возможности так, чтобы модули имели следующие желательные свойства:
- модуль должен иметь один вход и один выход и возвращать управление тому, кто его вызвал;
- размер модуля должен быть небольшим (10-100 операндов), при этом его легче читать и тестировать;
- модуль не должен сохранять историю своих вызовов для управления своим функционированием;
- модуль должен по возможности реализовывать одну функцию преобразования исходных данных в результат, это позволяет выделять часто употребляемые модули и объединять их в библиотеки;
- по возможности принятие решений в модулях нужно организовать так, чтобы эти решения прямо влияли только на выполнение вызываемых модулей;
- модуль должен быть по возможности независимым от других модулей, для этого важно ослабить связи между модулями.
Модули могут быть связаны:
- по содержимому, если один модуль ссылается на операторы другого модуля, используя абсолютное смещение, при этом даже перекомпиляция другим компилятором может привести к ошибке;
- по общей памяти, если два модуля ссылаются на одинаковые абсолютные адреса, при этом изменение размера одного элемента общей области памяти может привести к ошибкам в других модулях. Глобальные данные затрудняют чтение программ, поэтому количество глобальных данных надо стремиться уменьшать;
- по управлению – когда один модуль при вызове другого модуля может модифицировать его действия (это может возникнуть, если вызываемый модуль реализует несколько функций, при этом, если исправлять действие одной из функций, можно испортить и выполнение остальных);
- по данным, нужно стремиться передавать меньше данных в модули.