Учебник Технология программирования. Технология программирования
Скачать 7.85 Mb.
|
5. ПРОЕКТИРОВАНИЕ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ ПРИ СТРУКТУРНОМ ПОДХОДЕ Как уже упоминалось ранее, сущность структурного подхода заключается в декомпозиции программы или программной системы по функциональному принципу. Все предлагаемые методы декомпозиции используют интерфейсы простейшего типа: примитивные интерфейсы и традиционные меню. и рассчитаны на анализ и проектирование как структур данных, так и обрабатывающих их программ. Причем в большинстве случаев первичным считают проектирование обрабатывающих компонентов, проектирование же структур данных выполняют параллельно.Существует и альтернативный подход, при котором первичным считают проектирование данных, а обрабатывающие программы получают, анализируя полученные структуры данных. В любом случае проектирование программного обеспечения начинают с определения его структуры. 5.1. Разработка структурной и функциональной схем Процесс проектирования сложного программного обеспечения начинают с уточнения его структуры, т. е. определения структурных компонентов и связей между ними. Результат уточнения структуры может быть представлен в виде структурной и/или функциональной схем и описания (спецификаций) компонентов. Структурная схема разрабатываемого программного обеспечения. Структурной называют схему, отражающую состав и взаимодействие по управлению частей разрабатываемого программного обеспечения. Структурные схемы пакетов программ не информативны, поскольку организация программ и пакеты не предусматривает передачи управления между ними. Поэтому структурные схемы разрабатывают для каждой программы пакета, а список программ пакета определяют, анализируя функции, укачанные в техническом задании. Самый простой вид программного обеспечения - программа, которая в качестве структурных компонентов может включать только подпрограммы и 140 библиотеки ресурсов. Разработку структурной схемы программы обычно выполняют методом пошаговой детализации (см. § 5.2). Структурными компонентами программной системы или программного комплекса могут служить программы, подсистемы, базы данных, библиотеки ресурсов и т. п. Структурная схема программного комплекса демонстрирует передачу управления от программы-диспетчера соответствующей программе (рис. 5. 1). Структурная схема программной системы, как правило, показывает наличие подсистем или других структурных компонентов. В отличие от программного комплекса отдельные части (подсистемы) программной системы интенсивно обмениваются данными между собой и, возможно, с основной программой. Структурная же схема программной системы этого обычно не показывает (рис. 5.2). Более полное представление о проектируемом программном обеспечении с точки зрения взаимодействия его компонентов между собой и с внешней средой дает функциональная схема. Функциональная схема. Функциональная схема или схема данных (ГОСТ 19.701 - 90) - схема взаимодействия компонентов программного обеспечения 141 с описанием информационных потоков, состава данных в потоках и указанием используемых файлов и устройств. Для изображения функциональных схем используют специальные обозначения, установленные стандартом. Основные обозначения схем данных по ГОСТ 19.701-90 приведены в табл. 5.1. Функциональные схемы более информативны, чем структурные. На рис. 5.3 для сравнения приведены функциональные схемы программных комплексов и систем. 142 Все компоненты структурных и функциональных схем должны быть описаны. При структурном подходе особенно тщательно необходимо прорабатывать спецификации межпрограммных интерфейсов, так как от качества их описания зависит количество самых дорогостоящих ошибок. К самым дорогим относятся ошибки, обнаруживаемые при комплексном тестировании, 143 так как дляих устранения могут потребоваться серьезные измененияуже отлаженных текстов. 5.2. Использование метода пошаговой детализации для проектирования структуры программного обеспечения Структурный подход к программированию в том виде, в котором он был сформулирован в 70-х годах XX в., предлагал осуществлять декомпозицию программ методом пошаговой детализации. Результатом декомпозиции является структурная схема программы, которая представляет собой многоуровневую иерархическую схему взаимодействия подпрограмм по управлению. Минимально такая схема отображает два уровня иерархии, т. е. показывает общую структуру программы. Однако тот же метод позволяет получить структурные схемы с большим количеством уровней. Метод пошаговой детализации (см. § 1.3) реализует нисходящий подход (см. § 2.3) и базируется на основных конструкциях структурного программирования (см. § 2.4). Он предполагает пошаговую разработку алгоритма. Каждый шаг при этом включает разложение функции на подфункции. Так на первом этапе описывают решение поставленной задачи, выделяя общие подзадачи, на следующем аналогично описывают решение подзадач, формулируя при этом подзадачи следующего уровня. Таким образом, на каждом шаге происходит уточнение функций проектируемого программного обеспечения. Процесс продолжают, пока не доходят до подзадач, алгоритмы решения которых очевидны. Декомпозируя программу методом пошаговой детализации, следует придерживаться основного правила структурной декомпозиции, следующего из принципа вертикального управления: в первую очередь детализировать управляющие процессы декомпозируемого компонента, оставляя уточнение операций с данными напоследок. Это связано с тем, что приоритетная детализация управляющих процессов существенно упрощает структуру компонентов всех уровней иерархии и позволяет не отделять процесс принятия решения от ею выполнения: так, определив условие выбора некоторой альтернативы, сразу же вызывают модуль, ее реализующий. Детализация операций со структурами в последнюю очередь позволит отложить уточнение их спецификаций и обеспечит возможность относительно безболезненной моди4'икации -лих структур за счет сокращения количества модулей, зависящих от этих данных. Кроме этого, целесообразно придерживаться следующих рекомендаций: • не отделять операции инициализации и завершения от соответствующей обработки, так как модули инициализации и завершения имеют плохую связность (временную) и сильное сцепление (по управлению); 144 • не проектировать слишком специализированных или слишком универсальных модулей, так как проектирование излишне специальных модулей увеличивает их количество, а проектирование излишне универсальных модулей повышает их сложность; • избегать дублирования действий в различных модулях, так как при их изменении исправления придется вносить во все фрагменты программы, где они выполняются - в этом случае целесообразно просто реализовать эти действия в отдельном модуле; • группировать сообщения об ошибках в один модуль по типу библиотеки ресурсов, тогда будет легче согласовать формулировки, избежать дублирования сообщений, а также перевести сообщения на другой язык. При этом, описывая решение каждой задачи, желательно использовать не более 1-2-х структурных управляющих конструкций, таких, как цикл-пока или ветвление, что позволяет четче представить себе структуру организуемого вычислительного процесса. Пример 5.1. Разработать алгоритм программы построения графиков функций одной переменной на заданном интервале изменения аргумента [x 1 , х 2 ] при условии непрерывности функции на всем интервале определения. Примечание. Для того чтобы программировать построение графиков функций с точками разрыва первого и второго рода, необходимо аналитически исследовать заданные функции, что представляет собой отдельную и достаточно сложную задачу. Численными методами данная задача не решается. В общем виде задача построения графика функции ставитсякак задача отображения реального графика (рис. 5.4, о), выполненного в некотором масштабе, в соответствующее изображение в окнена экране (рис. 5.4, б). 145 Для построения графика необходимо определить масштабы по осям координат: , 2 1 2 1 x x wx wx m x − − = min max 2 1 y y wy wy m y − − = и координаты точек графика: ( ) ⎡ ⎤ , 1 1 wx m x x px x i i + ∗ − = ( ) ⎡ ⎤ , 1 max wy m y y py y i i + ∗ − = Шаг координатной сетки по вертикали и горизонтали при этом можно определить по формулам: , 1 2 x x nl wx wx lpm − = y y nl wy wy lpm 1 2 − = где nl x nl y - соответственно количество вертикальных и горизонтальных линий. Для разметки сетки необходимо определить шаги разметки по горизонтали и вертикали: , 1 2 x x nl x x lm − = y y nl x x lm min max − = Таким образом, для того чтобы построить график, необходимо задать функцию, интервал изменения аргумента [x 1 , х 2 ], на котором функция непрерывна, количество точек графика n, размер и положение окна экрана, в котором необходимо построить график: wx 1, wy 1, wx 2, wy 2 и количество линий сетки по горизонтали и вертикали nl x , nl y . Значения wx 1, wy 1, wx 2, wy 2, nl x , nl y можно задать, исходя из размера экрана, а интервал и число точек графика надо вводить. Разработку алгоритма выполняем методом пошаговой детализации, используя для записи псевдокод. Примем, что программа будет взаимодействовать с пользователем через традиционное иерархическое меню, которое содержит пункты: Функция, Отрезок, Шаг, Вид результата. Выполнить и Выход (см. рис. 8.5). Для каждого пункта этого меню необходимо реализовать сценарий, предусмотренный в техническом задании. Шаг 1. Определяем структуру управляющей программы, которая для нашею случая реализует работу с меню через клавиатур: 146 Программа. Инициализировать глобальные значения Вывести заголовок и меню Выполнять Если выбрана Команда то Выполнить Команду иначе Обработать нажатие клавиш управления Все - если до Команда = Выход Конец. Очистка экрана, вывод заголовка и меню, а также выбор Команды - операции сравнительно простые, следовательно, их можно не детализировать. Шаг 2. Детализируем операцию Выполнить команду: Выполнить Команду: Выбор Команда Функция: Ввести или выбрать формулу Fun Выполнить разбор формулы Отрезок: Ввести значения х1,х2 Шаг: Ввести значения h Вид результата: Ввести вид _результата Выполнить: Рассчитать значения функции Если Вид_результата = График то Построить график иначе Вывести таблицу Все – если Все – выбор Определим, какие фрагменты имеет смысл реализовать в виде подпрограмм. Во- первых, фрагмент Вывода заголовка и меню, так как это достаточно длинная линейная последовательность операторов и её выделение в отдельную процедуру позволит сократить управляющую программу. Во-вторых, фрагменты Разбор формулы, Расчет значений функции, Построение графика и Вывод таблицы, так как это достаточно сложные операции. Это – программы первого уровня, которые определяют структуру программы (рис. 5.5). Определим для этих программ интерфейсы по данным с основной программой, т.е. списки параметров. 147 Подпрограмма Вывод заголовка и меню параметров не предполагает. Подпрограмма Разбор формулы должна иметь два параметра: Fun - аналитическое задание функции, Tree - возвращаемый параметр - адрес дерева разбора. Подпрограмма Расчет значений функции должна получать адрес дерева разбора Tree, отрезок: значения х 1 и х 2 , а также шаг h. Обратно в программу она должна возвращать таблицу значений функции Х(n) и Y(n), где n - количество точек функции. Подпрограммы Вывода таблицы и Построения графика должны получать таблицу значений функции и количество точек. После уточнения имен переменных алгоритм основной программы будет выглядеть следующим образом: Программа. Вывод заголовка и меню Выполнять Если выбрана Команда то Выбор Команда Функция: Ввести или выбрать формулу Fun Разбор формулы (Fun; Var Tree) Отрезок: Ввести значения xl,x2 Шаг: Ввести значения h Вид результата: Ввести вид_результата Выполнить: Расчет значений функции (xl, x2, k, Tree; VarX,Y,n) 148 Если Вид_резулыпата=График то Построение графика(Х, Y, п) иначе Выводтаблицы(Х, Y, п) Все-если Все-выбор иначе Обработать нажатие клавиш управления Все-если до Команда=Выход Конец. На следующих шагах необходимо выполнить детализацию алгоритмов подпрограмм. Детализацию выполняют, пока алгоритм программы не станет полностью понятен. Один из возможных вариантов полной структурной схемы данной программы показан на рис. 5.6. Как уже упоминалось в § 2.4, использование метода пошаговой детализации обеспечивает высокий уровень технологичности разрабатываемого программного обеспечения, так как он позволяет использовать только структурные способы передачи управления. 149 Разбиение на модули при данном виде проектирования выполняется эвристически, исходя из рекомендуемых размеров модулей (20-60 строк) и сложности структуры (две-три вложенных управляющих конструкции). В принципе в качестве модуля (подпрограммы) можно реализовать решение подзадач, сформулированных на любом шаге процесса детализации, однако определяющую роль при разбиении программы на модули играют принципы обеспечения технологичности модулей, рассмотренные в § 2.2. Для анализа технологичности полученной иерархии модулей целесообразно использовать структурные карты Константайна или Джексона. 5.3. Структурные карты Константайна На структурной карте отношения между модулями представляют в виде графа, вершинам которого соответствуют модули и общие области данных, а дугам - межмодульные вызовы и обращения к общим областям данных. Различают четыре типа вершин (рис. 5.7): • модуль - подпрограмма, • подсистема - программа, • библиотека - совокупность подпрограмм, размещенных в отдельном модуле, • область данных - специальным образом оформленная совокупность данных, к которой возможно обращение извне. При этом отдельные части программной системы (программы, подпрограммы) могут вызываться последовательно, параллельно или как сопрограммы (рис. 5.8). Чаще всего используют последовательный вызов, при котором модули, передав управление, ожидают завершения выполнения вызванной программы или подпрограммы, чтобы продолжить прерванную обработку. Под параллельным вызовом понимают распараллеливание вычислений на нескольких вычислителях, когда при активизации другого процесса данный процесс продолжает работу (рис. 5.9, а). На однопроцессорных компьютерах в мультипрограммных средах в этом случае начинается попеременное выполнение соответствующих программ. Параллельные процессы бывают синхронные и асинхронные. Для синхронных процессов определяют точки синхронизации - моменты времени, когда производится обмен информацией 150 между процессами. Асинхронные процессы обмениваются информацией только в момент активизации параллельного процесса. Под вызовом сопрограммы понимают возможность поочередного выполнения двух одновременно запущенных программ, например, если одна программа подготовила пакет данных для вывода, то вторая может ее вывести, а затем перейти в состояние ожидания следующего пакета. Причем в мультипрограммных системах основная программа, передав данные, продолжает работать, а не переходит в состояние ожидания, как изображено на рис. 5.9. 151 Если стрелка, изображающая вызов, касается блока, то обращение происходит к модулю целиком, а если входит в блок, то - к элементу внутри модуля. При необходимости на структурной карте можно уточнить особые условия вызова (рис. 5.10): циклический вызов, условный вызов и однократный вызов - при повторном вызове основного модуля однократно вызываемый модуль не активизируется. Связи по данным и управлению обозначают стрелками, параллельными дуге вызова, направление стрелки указывает направление связи (рис. 5.11). Структурные карты Константина позволяют наглядно представить результат декомпозиции программы на модули и оценить ее качество, т. е. соответствие рекомендациям структурного программирования (сцепление и связность). Пример 5.2. Представим в виде структурной карты Константайна полную структурную схему, полученную в предыдущем примере (см. рис. 5.6). Подпрограммы Очистка окна, Вывод прямоугольника, Вывод строки текста, Вывод отрезка прямой, Задание цвета рисования и Задание цвета фона являются частью библиотеки графических примитивов практически в любой среде программирования универсального языка, поэтому их включать в структурную карту не будем. Для остальных подпрограмм покажем особые условия вызова и типы связей (рис. 5.12). 152 Модули Расчет значений функции, Вывод таблицы и Построение графика связаны с основной программой по образцу, так как параметры X и Y структурные (массивы), следовательно программа считается сцепленной по образцу. Анализ показывает, что количество сцеплений по образцу в программе можно уменьшить, если подпрограмму Расчет значений функции перенести на следующий уровень (рис. 5.13). Однако в этом случае при смене вида результата таблица значений будет рассчитываться заново. Аналогично можно перенести подпрограмму Разбор функции на более низкий уровень и вызывать ее, например, из подпрограммы Расчет значений функции, но поскольку велика вероятность многократного вычисления значений одной функции на разных интервалах, вряд ли это целесообразно. 153 После внесения соответствующих изменений в алгоритм следует определить полную спецификацию модулей. Спецификация должна включать: имя, краткое описание назначения, перечень входных и выходных параметров с указанием типа и области допустимых входных и выходных значений. Затем можно приступать к реализации модулей. В соответствии с требованиями нисходящей разработки (комбинированный подход) можно предложить следующий порядок реализации модулей: • Основная программа, • Вывод окна с текстом, • Вывод заголовка и меню, |