Добавил:
ivanov666
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз:
Предмет:
Файл:Технологии и методы программирования. Учебное пособие-1
.pdf
как лямбда-выражения и вывод типов, поддерживают функциональный
стиль программирования.
К императивной парадигме обычно относят структурное программирование, процедурное программирование, объектно-ориентированное программирование.
ОСНОВЫ СТРУКТУРНОГО ПРОГРАММИРОВАНИЯ
Структурное программирование – это использование определенных принципов и правил проектирования, разработки и оформления
программ с целью облегчения процессов их создания и тестирования,
повышения производительности труда программистов и улучшения читабельности результирующей программы. Структура программы и алгоритм решения задачи должны быть легкими для понимания, простыми для доказательства правильности и
удобными для модификации.
По своей сути структурный подход есть отказ от беспорядочного стиля
в алгоритмизации и программировании (в частности отказ от оператора
goto [16], недопущение так называемых спагетти-программ [53]) и определение ограниченного числа стандартных приемов построения легко
читаемых алгоритмов и программ с ясно выраженной структурой, что
особенно важно при разработке больших
программных систем.
Структурное программирование предполагает определенную организацию самого процесса программирования и технологию проектирования программ, что также положительно влияет на производительность труда программистов.
Теоретическим фундаментом структурного программирования является теорема о структурировании (теорема Бема–Якопини) [54], из которой следует, что алгоритм (программа) решения любой практически
вычислимой задачи может
быть представлен с использованием трех элементарных базисных управляющих структур: структуры следования
(последовательности); структуры ветвления, структуры цикла, изображенных на рис. 8.
Структура следования представляет собой естественный ход выполнения алгоритма – любую последовательность операторов, выполняющихся друг за другом (рис. 8, а).
Структура ветвления представляет блок принятия решения,
включает проверку некоторого логического
сти от результатов этой проверки, выполнение оператора
S
оператора
(рис. 8, б).
2
условия Р и, в зависимо-
S
либо
1
41

а б в
где Р – условие; S – оператор.
Рис. 8. Элементарные базисные управляющие структуры
Структура цикла (цикла с предусловием) представляет фактор по-
вторяемости вычислений, обеспечивает многократное повторение выполнения оператора S, пока выполняется (истинно) логическое условие
(рис. 8, в).
Базисный набор управляющих структур является функционально
полным, т. е. с его помощью можно описать любой сколь угодно сложный алгоритм, однако с целью создания более компактных
и наглядных
алгоритмов и программ используются дополнительные управляющие
структуры: структура сокращенного ветвления; структура варианта, или
многоальтернативного выбора; структура цикла со счетчиком; структура цикла с постусловием.
Для структурирования и понимания больших по объему программ
используются также дополнительные структурные средства, которые
поддерживают модульный принцип разработки ПС: это подпрограммы
и модули. Использование механизма
подпрограмм (процедур и функ-
ций) – это возможность выделять в самостоятельные программные еди-
ницы со своими входными и выходными данными отдельные (часто повторяющиеся) участки кода для последующего многократного вызова
их из различных точек программы и других подпрограмм.
Модуль представляет собой автономно компилируемую библио-
теку описаний типов, данных, процедур и
функций, что позволяет группировать описания данных и подпрограмм по их функциям и назначению согласно одному из основных принципов структурного программирования – разбиению больших задач на подзадачи.
42

ПРИНЦИПЫ СТРУКТУРНОГО ПРОГРАММИРОВАНИЯ
Становление и развитие структурного программирования связано
с именем Эдсгера Дейкстры [16].
Принцип 1. Следует отказаться от использования оператора без-
условного перехода goto.
Принцип 2. Любая программа строится из трех базовых управляю-
щих конструкций: последовательности, ветвления, цикла.
Последовательность – однократное выполнение операций в том по-
рядке, в котором они записаны в тексте программы. Бертран Мейер
поясняет: «Последовательное соединение: используйте выход одного элемента как вход к другому, подобно тому как электрики соединяют выход сопротивления со входом конденсатора» [17].
Ветвление – однократное выполнение одной из двух или более опе-
раций, в зависимости от выполнения заданного условия.
Цикл – многократное исполнение одной и той же операции до тех
пор
, пока выполняется заданное условие (условие продолжения цикла).
Принцип 3. В программе базовые управляющие конструкции могут
быть вложены друг в друга произвольным образом. Никаких других
средств управления последовательностью выполнения операций не
предусматривается.
Принцип 4. Повторяющиеся фрагменты программы можно оформить в виде подпрограмм (процедур и функций). Таким же образом
(в виде подпрограмм
) можно оформить логически целостные фраг-
менты программы, даже если они не повторяются. Бертран Мейер [55]
поясняет: «Преобразуйте элемент, возможно, с внутренними элементами, в подпрограмму, характеризуемую одним входом и одним выходом в потоке управления».
Принцип 5. Каждую логически законченную группу инструкций
следует оформить как блок. Блоки являются основой структурного программирования
.
Блок – это логически сгруппированная часть исходного кода, например набор инструкций, записанных подряд в исходном коде программы.
Понятие «блок» означает, что к блоку инструкций следует обращаться
как к единой инструкции. Блоки служат для ограничения области видимости переменных и функций. Блоки могут быть пустыми или вложенными один в другой.
Границы блока строго определены. Например,
в if-инструкции блок ограничен кодом BEGIN..END (в языке Pascal) или
фигурными скобками {...} (в языке C++, С#) или отступами (в языке
Python).
43

Принцип 6. Все перечисленные конструкции должны иметь один
вход и один выход.
Ограничение управляющими конструкциями с одним входом и одним выходом дает возможность построить произвольные алгоритмы
любой сложности с помощью простых и надежных механизмов.
Принцип 7. Разработка программы ведется пошагово, методом
«сверху вниз» [5].
ПРОЦЕДУРНОЕ ПРОГРАММИРОВАНИЕ
Процедурное программирование – парадигма программирования,
основанная на алгоритмической (процедурной) декомпозиции и концепции вызова процедуры. Процедурная декомпозиция состоит в том,
что задача, реализуемая программой, разделяется на подзадачи, а они
в свою очередь – на более мелкие этапы, т. е. выполняется пошаговая
детализация алгоритма решения задачи. Процедуры (подпрограммы,
методы, функции) содержат последовательность команд (операторов
для выполнения. В ходе выполнения программы любая процедура может быть вызвана из любой точки, включая саму данную процедуру (рекурсивный вызов).
Процедурное программирование широко используется в крупномасштабных проектах, когда на первый план выходят следующие преимущества:
возможность поддержки модульности и структурности;
легкость повторного использования фрагментов кода,
оформлен-
ных как процедуры (например, в библиотеках);
легкость прослеживания логики программы, что облегчает тести-
рование, отладку и сопровождение.
)
МОДУЛЬНОЕ ПРОГРАММИРОВАНИЕ
По мере усложнения программного обеспечения становится трудно
охватить и представить его как единое целое. Декомпозиция – разделение задачи на мелкие подзадачи, установление между ними связей и решение каждой из них независимо – приводит к построению программного обеспечения из отдельных программных модулей, каждый из которых представляет завершенную программу, решающую часть
общей
задачи.
44

Модульное программирование – это организация программы как совокупности небольших независимых блоков, называемых модулями.
Модуль – это самостоятельный, функционально законченный фрагмент программы, имеющий определенное назначение и обеспечивающий заданные функции обработки автономно от других программных
модулей.
Разделение программного обеспечения на модули позволяет упростить реализацию, облегчить понимание кода программы и работы
с данными, имеющими сложную структуру, а также избежать чрезмерной детализации алгоритмов больших программ, перенеся подробное
рассмотрение реализации тех или иных действий на алгоритмы модулей. Кроме того, модульная структура позволяет распределить работы
по исполнителям, обеспечив приемлемую их загрузку и требуемые
сроки разработки программных продуктов.
Иерархия модулей. Разделение модулей на
иерархические уровни
в структурном программировании производится по принципу вложенности. При этом используется вертикальное управление, для которого
характерно то, что обращение к любому модулю может происходить
только из какого-либо модуля более высокого уровня. Следовательно,
на верхнем уровне должен быть единственный модуль (ведущая программа), управление которым происходит из операционной системы
Взаимодействие равноуровневых модулей при вертикальном управлении происходит только через модуль более высокого уровня.
К модулям предъявляются следующие требования:
отдельная компиляция;
один вход и один выход – на входе программный модуль получает
определенный набор исходных данных, выполняет содержательную
обработку и возвращает один набор выходных данных, т. е. реализу-
стандартный принцип IPO (Input – Process – Output) – вход – про-
ется
цесс – выход;
выполнение минимального числа функций;
функциональная завершенность – модуль выполняет перечень ре-
гламентированных операций, достаточных для реализации каждой отдельной функции;
логическая независимость – результат работы программного мо-
дуля зависит только от исходных данных, но не зависит от источника
входных данных, места назначения выходных
данных и работы других
.
45

модулей. Другими словам, всё, что может изменить работу программы
(модуля), должно находиться вне программы (модуля);
слабые информационные связи с другими программными моду-
лями – обмен информацией между модулями должен быть по возможности минимизирован;
соответствие принципу вертикального управления;
возможность вызова других модулей;
обозримый по размеру и сложности
программный элемент;
независимость от истории вызовов (реентерабельность).
Степень независимости модулей оценивают двумя критериями:
сцеплением и связностью.
Сцепление является мерой взаимозависимости модулей, которая
определяет, насколько хорошо модули отделены друг от друга. Модули
независимы, если каждый из них не содержит никакой информации о
других модулях. Чем больше информации о других модулях хранит
модуль, тем больше он с ними сцеплен. Уменьшение зависимости модулей
улучшает технологичность проекта, поскольку чем выше степень независимости модулей, тем:
легче разобраться в отдельном модуле и всей программе и соот-
ветственно тестировать, отлаживать и модифицировать ее;
меньше вероятность появления новых ошибок при исправлении
старых или внесении
изменений в программу, т. е. вероятность появле-
ния «волнового» эффекта;
проще организовать разработку программного обеспечения груп-
пой программистов и легче его сопровождать.
Различают пять типов сцепления модулей:
по данным;
по образцу;
по управлению;
по общей области данных;
по содержимому.
Сцепление по данным предполагает, что модули
обмениваются дан-
ными, представленными скалярными значениями. При небольшом количестве передаваемых параметров этот тип обеспечивает наилучшие
технологические характеристики программного обеспечения.
Сцепление по образцу предполагает, что модули обмениваются
данными, объединенными в структуры. Этот тип также обеспечивает
неплохие характеристики, но они хуже, чем у предыдущего типа,
46

поскольку конкретные передаваемые данные «спрятаны» в структуры,
и потому уменьшается «прозрачность» связи между модулями. Кроме
того, при изменении структуры передаваемых данных необходимо модифицировать все использующие ее модули.
При сцеплении по управлению один модуль посылает другому некоторый информационный объект (флаг), предназначенный для управления внутренней логикой модуля. Таким способом часто
выполняют
настройку режимов работы программного обеспечения. Подобные
настройки снижают наглядность взаимодействия модулей и потому
обеспечивают еще худшие характеристики технологичности разрабатываемого программного обеспечения по сравнению с предыдущими
типами связей.
Сцепление по общей области данных предполагает, что модули ра-
ботают с общей областью данных.
программы, использующие этот тип сцепления, очень
сложны для
понимания при сопровождении программного обеспечения;
ошибка одного модуля, приводящая к изменению общих данных,
может проявиться при выполнении другого модуля, что существенно
усложняет локализацию ошибок;
при ссылке к данным в общей области модули используют кон-
кретные имена, что уменьшает гибкость разрабатываемого программного обеспечения.
Сцепления по содержимому – модуль
может содержать обращения
к внутренним компонентам другого модуля, т. е. фактически его фрагменты являются частью другого модуля, что противоречит блочноиерархическому подходу.
В табл. 1 показаны особенности модулей с разными типами сцепления.
Таблица 1
Устойчивость
Тип
сцепления
По данным Хорошая Хорошая Хорошая Большая
По образцу Средняя Хорошая Средняя Средняя
По управлению Средняя Плохая Плохая Малая
По общей области Плохая Плохая Средняя Малая
По содержимому Плохая Плохая Плохая Малая
к ошибкам
других моду-
лей
Наглядность
(понятность)
47
Возможность
изменения
Вероятность
повторного
использова-
ния

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

с последовательной связностью функций можно разбить на два модуля
или более как с последовательной, так и с функциональной связностью.
Такой модуль выполняет несколько функций, и, следовательно, его технологичность хуже: сложнее организовать тестирование, а при выполнении модификации мысленно приходится разделять функции модуля.
Информационно связанными считают функции, обрабатывающие
одни и те
же данные. При использовании структурных языков программирования раздельно выполнять функции можно, только если каждая
функция реализуется своей подпрограммой.
Поскольку все функции, работающие с некоторыми данными, собраны в одном месте, это позволяет при изменении формата данных
корректировать только один модуль, что обеспечивает неплохие показатели технологичности. Информационно связанными считают
также
данные, которые обрабатываются одной функцией.
Процедурно связаны функции или данные, которые являются частями одного процесса. Обычно модули с процедурной связностью
функций получают, если в модуле объединены функции альтернативных частей программы. При процедурной связности отдельные элементы модуля связаны крайне слабо, так как реализуемые ими действия
связаны лишь общим
процессом, следовательно, технологичность та-
кого вида связи ниже, чем предыдущего.
Временная связность функций подразумевает, что эти функции
выполняются параллельно или в течение некоторого периода. Временная связность данных означает, что они используются в некотором
временном интервале. Например, временную связность имеют функции, выполняемые при инициализации некоторого процесса. Отличительная особенность
временной связности в том, что действия, реализуемые такими функциями, обычно могут выполняться в любом порядке. Содержание модуля с временной связностью функций имеет
тенденцию меняться: в него могут включаться новые действия и/или
исключаться старые.
Большая вероятность модификации функций еще больше уменьшает показатели технологичности модулей этого вида по сравнению
с предыдущим.
Логическая связь базируется на объединении данных или функций
в одну логическую группу. В качестве примера можно привести
функции обработки текстовой информации или данные одного и того
же типа. Модуль с логической связностью функций часто реализует
49

альтернативные варианты одной операции, например сложение целых
чисел и сложение вещественных чисел. Из такого модуля всегда будет
вызываться одна какая-либо его часть, при этом вызывающий и вызываемый модуль будут связаны по управлению. Понять логику работы
модулей, содержащих логически связанные компоненты, как правило,
сложнее, чем модулей, использующих временную связность, следова
тельно, их показатели технологичности еще ниже.
В том случае, если связь между элементами мала или отсутствует,
считают, что они имеют случайную связность. Модуль, элементы которого связаны случайно, имеет самые низкие показатели технологичности, так как элементы, объединенные в нем, вообще не связаны.
В табл. 2 показаны особенности модулей с разными
видами связно-
сти.
Таблица 2
-
Вид связности
Функциональная Хорошая Хорошая Хорошая
Последовательная Хорошая Хорошая Хорошая
Информационная Средняя Средняя Средняя
Процедурная Средняя Средняя Плохая
Временная Средняя Средняя Плохая
Логическая Плохая Плохая Плохая
Случайная Плохая Плохая Плохая
Наглядность
(понятность)
Возможность
изменения
Сопровождаемость
ОБЪЕКТНО-ОРИЕНТИРОВАННОЕ ПРОГРАММИРОВАНИЕ
Объектно-ориентированное программирование (ООП) – парадигма
программирования, в которой основными концепциями являются понятия объектов и классов. При этом структура программы описывается
в терминах объектов и связей между ними, а поведение системы описывается в терминах обмена сообщениями между объектами.
Структура и поведение сходных объектов определяют общий для
них класс. Объекты одного
класса имеют одинаковое поведение и одинаковые характеристики (атрибуты), но значения их атрибутов могут
быть разными.
50
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
