Автор работы: Пользователь скрыл имя, 09 Декабря 2010 в 14:37, реферат
В основе того или иного языка программирования лежит некая руководящая идея, вызванная потребностями или, чаще всего, кризисом в области программирования и создания программного обеспечения, которая оказывает существенное влияние на стиль программирования и помогает преодолеть указанный кризис [9]. Рассмотрим вкратце историю появления и развития основных стилей программирования и процедурных алгоритмических языков.
ВВЕДЕНИЕ 3
1. ЦЕЛЬ МОДУЛЬНОГО ПРОГРАММИРОВАНИЯ 6
2. ОСНОВНЫЕ ХАРАКТЕРИСТИКИ ПРОГРАММНОГО МОДУЛЯ 7
3. ПРОЕКТИРОВАНИЕ МОДУЛЯ 11
3.1. ФУНКЦИОНАЛЬНАЯ ДЕКОМПОЗИЦИЯ 12
3.2. МИНИМИЗАЦИИ КОЛИЧЕСТВА ПЕРЕДАВАЕМЫХ ПАРАМЕТРОВ 12
3.3. МИНИМИЗАЦИИ КОЛИЧЕСТВА НЕОБХОДИМЫХ ВЫЗОВОВ 13
4. МЕТОДЫ РАЗРАБОТКИ СТРУКТУРЫ МОДУЛЬНОЙ ПРОГРАММЫ 16
4.1. МЕТОД ВОСХОДЯЩЕЙ РАЗРАБОТКИ 16
4.2. МЕТОД НИСХОДЯЩЕЙ РАЗРАБОТКИ 18
4.3. КОНСТРУКТИВНЫЙ И АРХИТЕКТУРНЫЙ ПОДХОДЫ 19
4.4. ДРУГИЕ МЕТОДЫ РАЗРАБОТКИ СТРУКТУРЫ МОДУЛЬНЫХ ПРОГРАММ И ИХ ОБЩАЯ КЛАССИФИКАЦИЯ 22
5. КОНТРОЛЬ СТРУКТУРЫ МОДУЛЬНОЙ ПРОГРАММЫ 25
ЗАКЛЮЧЕНИЕ 26
СПИСОК ЛИТЕРАТУРЫ: 27
В
связи с последней
Модульное проектирование относится к процессу расчленения больших проблем на более узкие, более управляемые подпроблемы. Первым шагом проектирования является решение, в каком месте должна быть граница между этими подпроблемами.
Для получения максимальных преимуществ от использования модульного программирования каждая подпроблема или модуль должны иметь один вход и один выход. В этом случае можно легко отслеживать поток управления в программе. В любом месте модуля должна иметься возможность увидеть точку входа в модуль и определить точное значение переменных и регистров в этой точке, а затем проследить функционирование модуля без тревоги об искажении программы. Один вход обеспечивает возврат потока управления в точку вызова. По этой причине, модульные программы почти всегда выполняются как структуры «CALL - RET».
Использование нескольких предложений RET в модуле не должно нарушать правило одного входа, поскольку все инструкции RET возвращают управление в одну и туже точку. Точно также, переход к общему RET в конце модуля, не изменяет его структуру, а добавляет лишь коды и увеличивает его сложность. С другой стороны, вход или выход из модуля не по этому правилу перечеркивает наибольшие преимущества модульного программирования: ясность и удобство сопровождения.
Имеется исключение из правила входа в модуль. Это происходит при использовании таблицы переходов для реализации потока управления внутри программы. Таблица перехода используется путем «проталкивания» адреса возврата в стек, вычисления индекса требуемого адреса перехода в таблице и выполнения перехода в памяти.
При практическом выполнении декомпозиции модулей можно самим найти некоторое количество альтернативных решений. Прежде чем осуществить правильный выбор, необходимо знать альтернативы. Цель состоит в выборе таких вариантов, которые создадут наилучшие условия проектирования.
При обращении к проблеме на стадии проектирования первым альтернативным выбором должна быть функциональная декомпозиция, т.е. разбиение проблемы на более узкие, вполне поддающиеся управлению функциональные единицы, где каждая единица выполняет завершенную, легко идентифицируемую задачу. Имеется множество путей определения содержания задачи. Вот лишь некоторые примеры подобных единиц, которые выполняют определенные функции: получение квадратного корня некоторого числа; выполнение всех операций относительно указанного устройства таких, как операции в/в диска, операции в/в клавиатуры; выполняющие общую группу действий в указанное время такие, как инициализация областей данных; и единицы, которые взаимодействуют последовательно или используют общие элементы данных такие, как считывание данных с клавиатуры и преобразование их в целые значения.
В настоящее время в области программирования на языках высокого уровня чаще всего принимаются такие решения, которые представляют собой наилучший способ по использованию сегментации программ. Часто обнаруживается, что некоторые модули связываются с помощью одного набора критериев, а другие модули - с помощью другого. Каждый модуль должен включать легко понимаемые программные секции.
Иногда обнаруживается, что после определения модулей программы создано нечто громоздкое и неуклюжее. Это часто случается тогда, когда модули при выполнении возложенных на них задач требуют доступа к обширному количеству данных. Чаще всего это легко может произойти, если на модуль возложено выполнение нескольких опций. Чтобы знать состояние программы в данное время, модуль должен принимать очень много различных переменных. Если это так, и выявлено, что модуль принимает большое количество параметров, необходимо ответить на следующие две группы вопросов:
Сегментация модулей через функциональный разрез часто происходит тогда, когда программист обнаруживает, что две программные секции идентичны или сильно похожи друг на друга. Программист затем пытается создать из них один модуль. Это не модульное программирование, поскольку результирующий модуль имеет не функциональное соединение.
Если в процессе проектирования будет обнаружено, что ничего сделать нельзя, чтобы избежать использования большого числа ссылок на данные или передачи меток параметров, надо вернуться обратно в начало проектирования и проверить корректность поставленной проблемы.
Одним из существенных преимуществ модульного программирования является то, что программа основного уровня очень часто может быть сконструирована для чтения как последовательность вызываемых процедур. Этот факт существенно повышает «понимаемость» программы, поскольку читатель может познакомиться с ее основным потоком и функционированием после прочтения только одной - двух страниц программного кода. Однако эта особенность может также иметь и недостатки. Одна из многих верхних статистических оценок программирования говорит о том, что 90% времени выполнения типовых программ расходуется в 10 % кода программы. При этом подразумевается, что если эти 10 % содержат большое количество цепочечных вызовов процедур, то суммарное время, затрачиваемое на управление выполнением программы, может стать непреодолимым препятствием на пути использования этого подхода.
Прежде чем отказаться от модульности проектируемой программы, надо проверить, что скрывается под зависимостью программы от времени. Во-первых, большинство программ затрачивают большую часть времени выполнения на ожидание ввода информации с клавиатуры. После нажатия клавиши требуемые функции, с точки зрения выполнения длительного процесса, обычно не расходуют время. Различие между 100 микросекунд и 100 миллисекунд для среднего пользователя неразличимо.
Противоположным для некоторых мнением является то, что действующий механизм пары CALL - RET не перекрывает потребляемое время. По сравнению с инструкциями перехода инструкция CALL выполняется на 30-50% дольше, а RET в среднем длиннее на 1 цикл. Только когда во внимание принимаются накладные расходы передачи параметров, сохранения регистров и т.д., называемые служебными расходами, модульные программы начинают выглядеть медленнее по сравнению с немодульными программами. В дополнение к тому, что модули модульных программ обычно являются более общими, чем их неструктурированные дубликаты, модули модульных программ могут использовать ссылки на память или стек с большей частотой. Дополнительное время, расходуемое на вычисление действительного адреса в теле модуля, может привести к замедлению выполнения конкретного модуля, чем узко закодированная конкретная программа.
Преимущества служебных программ и программ общего назначения заключаются в том, что модуль может быть использован виртуально в некотором месте программы. При написании немодульной программы программист может потратить несколько часов, пытаясь открыть: используется ли регистр (переменная), или хуже того, верно ли то, что он должен использоваться. При модульном программировании программист не интересуется тем, какие регистры (переменные) он использует в настоящий момент, пока вызываемый модуль копирует его параметры в стек и сохраняет весь набор регистров на входе. Эти особенности создают возможность сначала использовать приемы модульного программирования для повышения скорости кодирования и затем переработки программы для удаления "узких" мест.
Для областей, чувствительных к скорости работы, лучшей рекомендацией является выбор основной ветви программы. Если модуль упоминается только в чувствительной к скорости работы программной секции, то он может быть включен в "ветвь" внутри вызывающего модуля. Если другие секции используют модуль, то они могут быть скопированы в вызывающий модуль в необходимое место. В связи с тем, что основной вызывающий модуль станет большим, необходимо вставить комментарии в его тело, помечающие включаемый модуль как блок его владельца. Будущие читатели смогут затем прочитать комментарии для определения функций модуля и пропустить его мимо для возобновления чтения основного кода.
Как уже отмечалось выше, в качестве модульной структуры программы принято использовать древовидную структуру, включая деревья со сросшимися ветвями. В узлах такого дерева размещаются программные модули, а направленные дуги (стрелки) показывают статическую подчиненность модулей, т.е. каждая дуга показывает, что в тексте модуля, из которого она исходит, имеется ссылка на модуль, в который она входит. Другими словами, каждый модуль может обращаться к подчиненным ему модулям, т.е. выражается через эти модули. При этом модульная структура программы, в конечном счете, должна включать и совокупность спецификаций модулей, образующих эту программу. Спецификация программного модуля содержит:
В
процессе разработки программы ее модульная
структура может по-разному
Данный метод заключается в следующем. Сначала строится модульная структура программы в виде дерева. Затем поочередно программируются модули программы, начиная с модулей самого нижнего уровня (листья дерева модульной структуры программы), в таком порядке, чтобы для каждого программируемого модуля были уже запрограммированы все модули, к которым он может обращаться. После того, как все модули программы запрограммированы, производится их поочередное тестирование и отладка в принципе в таком же (восходящем) порядке, в каком велось их программирование. Такой порядок разработки программы на первый взгляд кажется вполне естественным: каждый модуль при программировании выражается через уже запрограммированные непосредственно подчиненные модули, а при тестировании использует уже отлаженные модули. Однако, современная технология не рекомендует такой порядок разработки программы. Во-первых, для программирования какого-либо модуля совсем не требуется наличия текстов используемых им модулей - для этого достаточно, чтобы каждый используемый модуль был лишь специфицирован (в объеме, позволяющем построить правильное обращение к нему), а для тестирования его возможно (и даже, как мы покажем ниже, полезно) используемые модули заменять их имитаторами (заглушками). Во-вторых, каждая программа в какой-то степени подчиняется некоторым внутренним для нее, но глобальным для ее модулей соображениям (принципам реализации, предположениям, структурам данных и т.п.), что определяет ее концептуальную целостность и формируется в процессе ее разработки. При восходящей разработке эта глобальная информация для модулей нижних уровней еще не ясна в полном объеме, поэтому очень часто приходится их перепрограммировать, когда при программировании других модулей производится существенное уточнение этой глобальной информации (например, изменяется глобальная структура данных). В-третьих, при восходящем тестировании для каждого модуля (кроме головного) приходится создавать ведущую программу (модуль), которая должна подготовить для тестируемого модуля необходимое состояние информационной среды и произвести требуемое обращение к нему. Это приводит к большому объему «отладочного» программирования и в то же время не дает никакой гарантии, что тестирование модулей производилось именно в тех условиях, в которых они будут выполняться в рабочей программе.