Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Kurs2011

.pdf
Скачиваний:
10
Добавлен:
15.06.2014
Размер:
615.51 Кб
Скачать

Омский государственный технический университет Кафедра «Конструирование и производство радиоаппаратуры»

В. Е. Осипов

ТЕХНОЛОГИЯ ПРОГРАММИРОВАНИЯ Материалы к курсовому проектированию

Омск 2006

ЦЕЛЬ КУРСОВОГО ПРОЕКТИРОВАНИЯ: изучение технологических приемов программирования, соответствующих структурному подходу к программированию.

Как показывает практика [4], «начинающие программисты, особенно студенты, часто пишут программы так: получив задание, тут же садятся за компьютер и начинают кодировать те фрагменты алгоритма, которые им удается придумать сразу. Переменным дают первые попавшиеся имена типа a, b, c или другие, более отражающие словарный запас автора, чем содержание величин. Когда компьютер зависает, безжалостно убивая первый порыв энтузиазма, делается перерыв, после которого написанные фрагменты стираются, и все повторяется заново.

Впроцессе работы несколько раз меняются структуры данных, функции нервно удаляются и кодируются заново, а разбиение на модули делается только тогда, когда листать программу становится утомительно. Нечего говорить, что комментарии к программе не пишутся, а ее текст никак не форматируется. Периодически высказываются сомнения в правильности работы компилятора, компьютера и операционной системы.

Когда программа впервые доходит до стадии выполнения, в нее вводится произвольные значения, после чего экран становится объектом пристального удивленного изучения. ˝Работает˝ такая программа обычно только в бережных руках хозяина на одном наборе исходных данных, а внесение даже небольших изменений может привести автора к потере веры в себя и ненависти к процессу программирования».

Внастоящее время выделяют четыре этапа развития программирования, различающиеся базовыми методами или подходами:

1)«стихийное» программирование (до середины 60-х годов XX в.);

2)структурный подход (60–70-е годы);

3)объектный подход (с середины 80-х до конца 90-х гг.);

4)компонентный подход и CASE-технологии (с середины 90-х гг. и по сей день).

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

В начале 60-х годов XX в. разразился «кризис программирования». Он выражался в том, что фирмы, взявшиеся за разработку сложного программного обеспечения, такого, как операционные системы, срывали все сроки завершения проектов. Проект устаревал раньше, чем был готов к внедрению, увеличивалась его стоимость, и в результате многие проекты так никогда и не были завершены.

Объективно все это было вызвано несовершенством технологии программирования. Прежде всего, стихийно использовался подход «снизу-вверх», при котором вначале проектировали и реализовывали сравнительно простые подпрограммы, из которых затем пытались построить сложную программу. При сборке программного

2

продукта выявлялось большое количество ошибок согласования. Исправление таких ошибок, как правило, требовало серьезного изменения уже разработанных подпрограмм, что еще более усложняло ситуацию, так как при этом в программу часто вносились новые ошибки, которые необходимо было исправлять… В конечном итоге процесс тестирования и отладки программ занимал более 80 % времени разработки, если вообще когда-нибудь заканчивался. На повестке дня стоял вопрос разработки технологии создания сложных программных продуктов, снижающей вероятность ошибок проектирования.

Структурный подход к программированию представляет собой совокупность рекомендуемых технологических приемов, охватывающих выполнение всех этапов разработки программного обеспечения (в том числе – этапов: анализа, проектирования, реализации). В основе структурного подхода лежит процедурная декомпозиция (разбиение на части) сложных систем с целью последующей реализации в виде отдельных небольших (до 40 – 50 операторов) подпрограмм. В отличие от используемого ранее процедурного подхода к декомпозиции, структурный подход требовал представления задачи в виде иерархии подзадач простейшей структуры1, рис. 1. Проектирование осуществляется «сверху-вниз» и подразумевает реализацию общей идеи, обеспечивая проработку интерфейсов подпрограмм.

Стремление разграничить доступ к глобальным данным (с целью уменьшения вероятности их непреднамеренного искажения) привело к появлению технологии модульного программирования, при которой создаются отдельно компилируемые модули (библиотеки подпрограмм). Связи между модулями осуществляется через специальный интерфейс, в то время как доступ к реализации модуля (телам подпрограмм и некоторым внутренним переменным) запрещен.

Практика показала, что структурный подход в сочетании с модульным программированием позволяет получить достаточно надежные программы, размер которых не превышает 10 000 операторов. Узким местом модульного программирования является то, что ошибка в интерфейсе при вызове подпрограммы выявляется только при выполнении подпрограммы (из-за раздельной компиляции модулей обнаружить эти ошибки раньше невозможно). А при увеличении размера программы обычно возрастает сложность межмодульных интерфейсов. Для разработки программного обеспечения большого объема было предложено использовать объектный подход.

1 Наиболее простым видом программ считают программы, которые в качестве структурных компонентов включают только подпрограммы и библиотеки ресурсов.

3

 

 

 

 

 

 

 

 

 

Объект

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Блок 1

 

 

 

 

Блок 2

 

 

Блок n

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

...

 

 

...

 

 

 

 

...

 

 

 

 

 

 

 

 

 

 

 

Блок 1.1

 

Блок 1.k

 

Блок 2.1

 

Блок 2.m

 

Блок n.1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Детализация

Абстракция

 

 

Блок n.i

Уровень

0

Уровень

1

Уровень

2

Рисунок 1. Блочно-иерархический подход к созданию сложных систем

Далее мы вкратце остановимся на отдельных аспектах структурного программирования.

В соответствии с ГОСТ 19.102–77 «Стадии разработки» различают следующие стадии разработки программного обеспечения (далее – ПО):

постановка задачи (стадия «Техническое задание»). Включает четкую формулировку назначения ПО и основные требования к нему: функциональные, эксплуатационные;

анализ требований и разработка спецификаций (стадия «Эскизный про-

ект»). Подразумевает создание внешней спецификации2 ПО, в том числе – описание исходных данных и результатов (типы, форматы, точность, ограничения); проектирование (стадия «Технический проект»). Процесс проектирования

сложного ПО обычно включает:

oпроектирование общей структуры – определение основных компонентов и их взаимосвязей;

o декомпозицию компонентов и построение структурных иерархий; o проектирование компонентов.

Результатом проектирования является детальная модель разрабатываемого ПО вместе со спецификациями его компонентов всех уровней. Модель представляется в виде структурных схем, словесного описания. На этом этапе применяется технология нисходящего проектирования, при которой исполь-

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

реализация (стадия «Рабочий проект») представляет собой процесс поэтапного написания кодов программы на выбранном языке прогрммирования (кодирование), их тестирование и отладку.

Нисходящий подход. Нисходящий подход предполагает, что проектирование и последующая реализация компонентов выполняется «сверху-вниз», т. е. вначале

2 Спецификациями называют точное формализованное описание функций и ограничений разрабатываемого ПО.

4

проектируют компоненты верхних уровней иерархии, рисунок 1, затем следующих и так далее до самых нижних уровней. В той же последовательности выполняют и реализацию компонентов. При этом в процессе программирования компоненты нижних, еще не реализованных уровней заменяют специально разработанными отладочными модулями – «заглушками», что позволяет тестировать и отлаживать уже реализованную часть. Заглушка имитирует работу заменяемого компонента; в простейшем случае она просто выдает сообщение о том, что ей передано управление, а затем передает управление в вызывающий модуль; в других случаях заглушка может выдать значения, заданные заранее или вычисленные по упрощенному алгоритму.

При использовании нисходящего подхода применяют иерархический, операционный и комбинированный методы определения последовательности проектирования и реализации компонентов.

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

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

Комбинированный метод учитывает следующие факторы, влияющие на последовательность разработки:

достижимость модуля – наличие всех модулей в цепочке вызова данного модуля;

зависимость по данным – модули, формирующие некоторые данные, должны создаваться раньше обрабатывающих;

обеспечение возможности выдачи результатов – модули вывода результатов должны создаваться раньше обрабатывающих;

готовность вспомогательных модулей – вспомогательные модули, например, модули закрытия файлов, завершения программы, должны создаваться раньше обрабатывающих;

наличие необходимых ресурсов.

Нисходящий подход допускает нарушение нисходящей последовательности разработки компонентов в специально оговоренных случаях. Так, если некоторый компонент нижнего уровня используется многими компонентами более высоких уровней.

5

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

Принцип вертикального управления: требования к структуре программы.

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

Требования к структурным компонентам. Структурная декомпозиция должна осуществляться таким образом, чтобы отдельные компоненты в иерархии подпрограмм (рисунок 1) имели наименьшую взаимную зависимость (сцепление), а связи внутри компонентов были наиболее прочными (была велика связность). При таких требованиях легче разобраться в отдельном блоке и всей программе, легче тестировать, отлаживать, модифицировать и проще организовать разработку группой программистов – в общем, увеличивается технологичность ПО.

Блоки являются независимыми (имеют наименьшее сцепление), если один не содержат ни какой информации о другом. Чем больше информации хранит о других блок, тем больше он с ними сцеплен. Различают пять типов сцепления модулей:

o по данным; o по образцу;

o по управлению;

o по общей области данных; o по содержимому.

Сцепление по данным предполагают, что модули обмениваются данными, представленными скалярными значениями. При небольшом количестве передаваемых параметров этот тип обеспечивает наилучшие технологические характеристики ПО. Например, функция Max предполагает сцепление по данным через параметры скалярного типа:

Function Max(a, b: integer): integer; begin

if a>b then Max:=a else Max:=b;

end;

Сцепление по образцу предполагает, что блоки обмениваются данными, объединенными в структуры. Этот тип обеспечивает неплохие характеристики, но они хуже, чему предыдущего типа, так как конкретные передаваемые данные «спрятаны» в структуры и поэтому уменьшается «прозрачность» связи между блоками. Кроме того, при изменении структуры передаваемых данных необходимо модифицировать

6

все использующие ее блоки. Так, функция MaxEl, описанная ниже, предполагает сцепление по образцу (параметр а – открытый массив).

Function MaxEl(a: array of integer): integer; Var i: word;

begin MaxEl:=a[0];

for i:= 1 to High(a) do

if a[i]>MaxEl then MaxEl:=a[i];

end;

При сцеплении по управлению одна подпрограмма посылает другой некоторый информационный объект (флаг), предназначенный для управления внутренней логикой модуля. Подобные связи снижают наглядность взаимодействия модулей и потому обеспечивают еще худшие характеристики технологичности по сравнению с предыдущими типами связей. Например, функция MinMax предполагает сцепление по управлению, так как значение параметра flag влияет на логику программы: если функция MinMax пролучает значение параметра flag, равное true, то возвращает максимальное значение из двух а если false, то минимальное:

Function МinMax(a, b: integer; flag: boolean): integer; begin

if (a>b) and (flag) then MinMax:=a else MinMax:=b;

end;

Сцепление по общей области данных предполагает, что подпрограммы работают с общей областью данных. Этот тип сцепления является недопустимым, поскольку:

программы, использующие данный тип сцепления, очень сложны для понимания при сопровождении ПО;

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

Например, функция MaxA, использующая глобальный массив А, сцеплена с основной программой по общей области:

Function MaxA: integer; Var i: word;

begin MaxA:=a[Low(a)];

for i:= Low(a)+1 to High(a) do if a[i]>MaxA then MaxA:=a[i];

end;

В случае сцепления по содержимому один блок содержит обращения к внутренним компонентам другого (передает управление внутрь, читает и/или изменяет внутренние данные), что полностью противоречит блочно-иерархичному подходу.

7

Отдельная подпрограмма в этом случае уже не является блоком («черным ящиком»): ее содержимое должно учитываться при разработке другой подпрограммы. Универсальные языки процедурного программирования, например Pascal, данного типа сцепления в явном виде не поддерживают, но для языков низкого уровня, например Ассемблера, такой вид сцепления остается возможным.

Использование метода пошаговой детализации для проектирования струк-

туры ПО. Структурный подход к программированию предполагает на этапе проектирования получение структурной схемы ПО (рис. 1) методом пошаговой детализации. Данный метод реализует нисходящий подход и базируется на основных конструкциях структурного программирования. Метод предполагает пошаговую разработку алгоритма; каждый шаг при этом включает разложение функций на подфункции. Так на первом этапе описывают решение поставленной задачи, выделяя общие подзадачи, на следующих этапах аналогично описывают решение подзадач до тех пор, пока не доходят до подзадач, алгоритмы решения которых – очевидны. В ходе такой декомпозиции рекомендуют придерживаться основного правила структурной декомпозиции: в первую очередь детализировать управляющие процессы декомпозируемого компонента, оставляя операции с данными напоследок.

Пример 11.1. Разработать структурную схему программы, предназначенной для решения следующей задачи.

Дан одномерный массив целых чисел, записанный в текстовый файл. Отсортировать часть элементов по возрастанию либо по убыванию значений, в зависимости от суммы цифр числа элементов в массиве:

-если сумма больше 5 – по возрастанию;

-если меньше – по убыванию;

-если равна – не сортировать.

Если количество элементов с четными значениями не меньше количества с нечетными, то сортируются положительные элементы, а если меньше – отрицательные. В выходном массиве отрицательные элементы остаются на месте отрицательных в исходном массиве. Отсортированный массив записать в другой текстовый файл.

Решение. Шаг 1. Структуру основной программы определяем как состоящую из трех подпрограмм:

Ввод массива (из файла с определением размера массива), Обработка массива, Вывод массива (в файл), рисунок 2.

8

Программа сортировки массива

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Ввод

 

 

 

Обработка

 

 

 

 

 

 

Вывод

 

 

 

 

 

 

 

 

 

 

массива

 

 

массива

 

 

 

 

 

массива

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Выбор

 

 

 

Сорти-

 

 

 

Возврат

 

 

 

Анализ

 

 

элементов для

 

 

 

 

 

отсортированных

 

 

 

 

 

 

 

 

 

ровка

 

 

 

 

 

 

 

 

 

 

 

 

 

сортировки

 

 

 

 

 

 

 

элементов

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Нахождение

 

 

 

 

 

Нахождение

 

 

 

Сортировка по

 

Сортировка по

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

суммы цифр числа

 

 

 

 

 

количества

 

 

 

 

возрастанию

 

 

убыванию

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

элементов

 

 

 

нечетных эл-тов

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Рисунок 2. Структурная схема программы сортировки массива

Шаг 2. Подпрограммы Ввод массива и Вывод массива дальнейшей декомпозиции не требуют и могут быть описаны простыми алгоритмами. Подпрограмму Обработка массива разбиваем на следующие подзадачи:

Анализ (определяет, требуется ли сортировка элементов, а если требуется, то какие элементы необходимо сортировать и в каком направлении);

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

По результатам выполнения подпрограммы Анализ процедура Обработка массива принимает решение, требуется ли выполнение остальных процедур обработки.

Шаг 3. Подпрограмму Анализ декомпозируем на следующие подзадачи: Нахождение суммы цифр числа элементов; Нахождение количества нечетных элементов.

Подпрограмму Сортировка декомпозируем на подзадачи: Сортировка по возрастанию; Сортировка по убыванию.

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

9

Структурные карты Константайна. В соответствии с основным правилом структурной декомпозиции, выполнив декомпозицию процессов, рисунок 2, переходим к уточнению операций с данными, для чего удобно использовать структурные карты Константайна. На структурной карте Константайна отношения между модулями представляем в виде графа, вершинам которого будут соответствовать модули, а дугам – межмодульные вызовы. При этом мы рассмотрим используемые чаще всего последовательные вызовы, рисунок 3а, при которых модули, передав управление, ожидают завершения выполнения вызванной подпрограммы.

A

 

A

 

A

 

A

 

 

 

 

 

 

 

1

B

 

B

 

B

 

C

 

B

 

 

 

 

 

 

 

 

 

а)

 

б)

 

 

в)

 

г)

Рисунок 3. Обозначения последовательного вызова и особых условий его осуществления:

а) - без указания условий; б) - циклический; в) - условный; г) - однократный

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

ля однократно вызываемый модуль не

 

 

 

 

 

 

 

 

 

активизируется.

 

 

 

 

 

 

 

 

 

 

 

 

 

А

 

 

 

А

 

 

Связи по данным и управлению обознача-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ют стрелками, параллельными дуге вызова,

 

 

 

 

 

 

 

 

 

направление

стрелки указывает направление

x, y

z

связи, рисунок 4.

 

 

 

k

 

 

 

 

 

 

 

 

 

 

Структурные

карты Константайна позво-

 

 

 

 

 

 

 

 

 

 

 

B

 

 

 

B

 

ляют наглядно представить результат деком-

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

позиции программы на модули и оценить ее

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

качество, т.

е.

соответствие рекомендациям

 

 

а)

 

 

 

б)

структурного программирования (сцепление и

Рисунок 4. Обозначение типа связи:

связность).

 

 

 

 

 

 

 

 

 

 

 

 

 

Пример 11.2. Представить в виде струк-

а) - по данным; б) - по управлению

 

турной карты Константайна структурную схему, полученную в предыдущем примере.

Результат представлен на рисунке 5. Поясним некоторые связи, изображенные на карте.

В результате выполнения процедура Ввод массива возвращает в основную программу считанный из файла массив A и подсчитанное количество элементов n, содержащихся в исходном файле.

10

Соседние файлы в предмете Информатика