Тема 2. Технология структурного программирования
2.1 Парадигмы программирования
С начала развития программирования подходы к написанию кода менялись по разным причинам: разработчики находили более удобные решения, появлялись новые технологии и задачи. Условно, в одной и той же сфере код 20 лет назад писали иначе, чем сейчас.
На разных этапах формировались и продолжают формироваться стили написания кода и поиска решений. Разработчики определили и структурировали подходы к решению задач, объединив их в понятные правила и назвали парадигмами программирования.
Парадигмы — это не строгие законы, а скорее стили написания кода и использования языковых возможностей. Работа в рамках парадигмы означает, что разработчик придерживается использования определённой архитектуры и инструментов языка. Таким образом, парадигма программирования указывает не столько на то, что нужно делать, сколько на то, чего делать не следует (или крайне не рекомендуется).
Существуют два ключевых определения парадигмы:
Языки программирования конструируются на основе этих правил и у каждого языка есть свой набор парадигм, которым следуют при разработке.
Лишь небольшое количество языков строго соответствует одной парадигме, но среди популярных языков общего назначения таких нет. Современные языки позволяют использовать несколько парадигм одновременно или переключаться между ними в зависимости от задачи.
Парадигмы программирования определяют стандарты написания кода и при переходе на другой язык, поддерживающий знакомую парадигму, специалисту легче преодолеть «языковой барьер»
Одна и та же задача может быть решена по-разному: каждая парадигма подразумевает своё решение. (Рис.)

Рис.
Паради́гма программи́рования — совокупность идей и понятий, определяющих стиль написания компьютерных программ (подход к программированию). Это способ концептуализации, который определяет организацию вычислений и структурирование работы, выполняемой компьютером.
За время развития программирования появилось много парадигм, и новые продолжают возникать из старых в ответ на обновление инструментов и появление новых задач.
Две основные парадигмы, императивная и декларативная, включают в себя множество других.
Императивная парадигма.
Императивная парадигма - первая появившаяся из парадигм сформировалась, когда программы записывались на машинном языке. Написанный в соответствии с этой парадигмой код будет отвечать на вопрос: «Как именно должна работать программа и что она должна делать?»
Для императивного программирования характерны следующие черты:
Чистый императивный код — машинный язык, неудобный для написания программ из-за сложности восприятия, дублирования фрагментов и частого использования операторов присваивания. Как правило, императивное программирование решает небольшие подзадачи для поддержания работы основной программы.
Эту парадигму (но не ограничиваясь ею) можно использовать почти во всех современных языках: C, C#, C++, Python, Java, Ruby.
К императивной парадигме относятся следующие виды программирования:
- процедурное (примеры языков: C, Pascal, COBOL, ALGOL, BASIC, Fortran.)
- структурное;
- модульное
- аспектно-ориентированное;
- объектно-ориентированное и другие (Java, Python, C++, Ruby, C#, Objective-C, PHP.).
Также императивную парадигму программирования можно считать более низкоуровневой, потому что программисту необходимо понимать, как работают программы на уровне инструкций.
Декларативная парадигма.
Если императивное программирование подразумевает максимально подробные инструкции для машины, то декларативная парадигма работает по-другому: она описывает не способ решения, а проблему и желаемый результат, но не пишет никаких инструкций.
Она отвечает на вопрос: «Что делает программа и что должно получиться в итоге?»
Как это выглядит:
Декларативный подход существенно облегчает работу. Его преимущества можно свести к двум основным:
К декларативной парадигме относятся:
- функциональное программирование (Haskell, Erlang, Scala, F#, OCaml, ELM, Lisp.)
- логическое программирование (Prolog).
Сравнение и выбор парадигмы
Зачем использовать императивное программирование, если декларативное позволяет писать гораздо меньше кода? Есть несколько причин.
Во-первых, декларативное программирование — это обёртка для императивного. Компьютер не может сам ппонять, чего хочет программист, поэтому для него нужно написать конкретные инструкции, что и в каком порядке делать.
Например, когда вы пишете запрос в SQL, его выполнение происходит по заранее описанным инструкциям. Поэтому нам нужно императивное программирование, чтобы заставить работать декларативное.
Во-вторых, декларативное программирование не подходит для задач, требующих доступа к состоянию программы. Например, если нужно проверить, нажата ли кнопка или поставлена ли галочка в чекбокс.
В декларативном программировании нельзя отследить ни само состояние, ни его изменение, поэтому не получится указать, что должно происходить в ответ на действия пользователя.
В-третьих, императивное программирование даёт больше свободы и контроля, поэтому его чаще используют в творческих областях, особенно там, где важен порядок выполняемых действий.
Каждая парадигма подходит для определённых задач:
Вопрос «Какая парадигма лучше?» некорректен: все они эффективны, если используются по назначению. Современные языки часто позволяют комбинировать парадигмы, выбирая оптимальный подход для каждой части программы.
|
Критерий |
Императивный подход |
Декларативный подход |
|
Фокус |
Как сделать? |
Что получить? |
|
Контроль |
Полный контроль над выполнением |
Контроль делегирован системе |
|
Пример |
Цикл для перебора массива |
SQL-запрос |
|
Плюсы |
Гибкость, эффективность |
Краткость, надёжность, читаемость |
|
Минусы |
Много кода, сложность |
Меньше контроля, не для всех задач |
Задание 1. Дать определение и писать суть:
Основные модели программирования
‒ Функциональное программирование
‒ Логическое программирование
- Реактивное программирование
- Параллельное и распределённое программирование
‒ Объектно-ориентированное программирование:
o Компонентно-ориентированное программирование
o Прототипно-ориентированное программирование
o Агентно-ориентированное программирование
Подходы и приёмы программирования
‒ Процедурное программирование
‒ Аппликативное программирование
‒ Обобщённое программирование
‒ Доказательное программирование
- Метапрограммирование
‒ Порождающее программирование
‒ Аспектно-ориентированное программирование
‒ Агентно-ориентированное программирование
‒ Контрактное программирование
‒ Рекурсия
‒ Автоматное программирование
‒ Событийно-ориентированное программирование
‒ Компонентно-ориентированное программирование
‒ Грамотное программирование
‒ Вайб-кодинг
Основные модели программирования:
Функциональное программирование
Суть: Парадигма, рассматривающая вычисления как вычисление значений математических функций и избегающая изменяемых состояний и данных. Программа представляет собой композицию функций.
Ключевые принципы:
Особенности:
Языки: Haskell,
Lisp, Scheme, Erlang, Scala, F#, OCaml, Clojure
Применение: Обработка данных, математические вычисления, финансовые
системы, компиляторы
Логическое программирование
Суть: Парадигма, основанная на формальной логике, где программа представляет собой набор фактов и правил, а вычисление — процесс логического вывода.
Ключевые принципы:
Особенности:
Языки: Prolog,
Mercury, Datalog
Применение: Экспертные системы, искусственный интеллект, базы
знаний, лингвистический анализ, формальная верификация
Реактивное программирование
Суть: Парадигма, ориентированная на потоки данных и автоматическое распространение изменений. Реакция на изменения данных происходит автоматически.
Ключевые принципы:
Особенности:
Языки/библиотеки: RxJS
(JavaScript), ReactiveX, Akka Streams (Scala), Reactor (Java), Combine (Swift)
Применение: Веб-интерфейсы (Angular, React), мобильные приложения,
обработка событий в реальном времени, IoT
Параллельное и распределённое программирование
Суть: Модели и техники для одновременного выполнения нескольких вычислений на нескольких процессорах или компьютерах.
Параллельное программирование:
Распределённое программирование:
Ключевые подходы:
Особенности:
Языки/платформы: Erlang/Elixir
(Actor), Go (goroutines и channels), MPI (C/Fortran), Hadoop/Spark, CUDA (GPU)
Применение: Высокопроизводительные вычисления, Big Data,
веб-серверы, научные симуляции, блокчейн
Объектно-ориентированное программирование (ООП)
Суть: Парадигма, основанная на концепции объектов, которые содержат данные (поля) и методы для работы с этими данными.
Основные принципы:
Компонентно-ориентированное программирование
Суть: Расширение ООП, где компоненты — это независимые, повторно используемые модули с чётко определёнными интерфейсами.
Особенности:
Примеры: COM/DCOM,
CORBA, .NET Components, JavaBeans, EJB
Применение: Корпоративные системы, распределённые приложения,
плагинные архитектуры
Прототипно-ориентированное программирование
Суть: Объекты создаются не из классов, а путём клонирования существующих объектов-прототипов.
Особенности:
Языки: JavaScript,
Lua, Self, Io
Применение: Веб-разработка (JavaScript), скриптовые языки, гибкие
динамические системы
Агентно-ориентированное программирование
Суть: Подход, где программа состоит из автономных агентов, взаимодействующих друг с другом для решения задач.
Особенности:
Применение: Искусственный интеллект, мультиагентные системы, робототехника, моделирование социальных систем
Подходы и приёмы программирования:
Процедурное программирование
Суть: Программа представляется как последовательность процедур (подпрограмм), которые могут вызывать друг друга.
Особенности:
Языки: C, Pascal, Fortran, BASIC
Применение: Системное программирование, научные вычисления, обучение основам программирования
Аппликативное программирование
Суть: Подход, основанный на применении функций к аргументам. Часто используется как синоним функционального программирования, но с акцентом на применение функций.
Особенности:
Обобщённое программирование
Суть: Разработка алгоритмов и структур данных, работающих с различными типами данных без потери типобезопасности.
Ключевые техники:
Преимущества:
Применение: Контейнеры (векторы, списки), алгоритмы (сортировка, поиск), библиотеки
Доказательное программирование
Суть: Подход, при котором корректность программы доказывается формально с использованием математических методов.
Техники:
Языки/инструменты: Coq,
Agda, Isabelle, TLA+
Применение: Критически важные системы (авионика, медицина),
компиляторы, криптография
Метапрограммирование
Суть: Написание программ, которые создают или модифицируют другие программы (или самих себя) во время компиляции или выполнения.
Формы метапрограммирования:
Применение:
Порождающее программирование
Суть: Автоматическая генерация программного обеспечения на основе высокоуровневых спецификаций, моделей или шаблонов.
Подходы:
Инструменты: UML-генераторы
кода, Yeoman, Cookiecutter, шаблоны проектов
Применение: Быстрая разработка приложений, создание каркасов
проектов, кодогенерация для повторяющихся задач
Аспектно-ориентированное программирование (АОП)
Суть: Разделение сквозной функциональности (логирование, безопасность, транзакции) в отдельные модули — аспекты.
Ключевые концепции:
Языки/фреймворки: AspectJ
(Java), Spring AOP, PostSharp (C#)
Применение: Логирование, безопасность, управление транзакциями,
кэширование, обработка ошибок
Контрактное программирование
Суть: Определение формальных спецификаций (контрактов) для взаимодействия между компонентами программы.
Элементы контракта:
Языки с поддержкой: Eiffel,
D, Spec#; библиотеки для Java, C++, Python
Преимущества:
Рекурсия
Суть: Техника, при которой функция вызывает саму себя либо непосредственно, либо косвенно через другие функции.
Типы рекурсии:
Применение:
Проблемы: Риск переполнения стека, неэффективность при глубокой рекурсии
Автоматное программирование
Суть: Организация программы как конечного автомата с явно определёнными состояниями и переходами.
Ключевые элементы:
Реализации:
Применение: Протоколы связи, пользовательские интерфейсы, игровые AI, бизнес-процессы
Событийно-ориентированное программирование
Суть: Архитектура, в которой поток выполнения программы определяется событиями.
Ключевые механизмы:
Применение:
Грамотное программирование
Суть: Подход, при котором программа и её документация пишутся как единый документ, предназначенный для чтения человеком.
Особенности:
Инструменты: Knuth's
WEB (Pascal), CWEB (C), noweb, Jupyter Notebooks
Применение: Образовательные материалы, научные вычисления, сложные
алгоритмические задачи
Вайб-кодинг
Суть: Современный неформальный термин для программирования, ориентированного на атмосферу, потоковое состояние и личный комфорт разработчика.
Характеристики:
Проявления:
Значение: Смещение фокуса с чистой продуктивности на удовольствие от процесса, творчество и благополучие разработчика.
2.2. Структурное программирование
Структу́рное программи́рование — парадигма программирования, в основе которой лежит представление программы в виде иерархической структуры блоков.
В соответствии с парадигмой, любая программа, которая строится без использования оператора goto - состоит из трёх базовых управляющих конструкций: последовательность, ветвление, цикл, а также используются подпрограммы.
При этом разработка программы ведётся пошагово, методом «сверху вниз».
Методология структурного программирования появилась как следствие возрастания сложности решаемых на компьютерах задач, и соответственно, усложнения программного обеспечения.
В 1970-е годы объёмы и сложность программ достигли такого уровня, что традиционная (неструктурированная) разработка программ перестала удовлетворять потребностям практики. Программы становились слишком сложными, чтобы их можно было нормально сопровождать. Поэтому потребовалась систематизация процесса разработки и структуры программ.
Структурное программирование основывается на фундаменте теоремы Бёма-Якопини, математически обосновывающей возможность структурной организации программ, и работы Эдсгера Дейкстры «О вреде оператора goto» (англ. Goto considered harmful).
Таким образом структурное программирование стало основой всего, что сделано в последующих методологиях программирования, включая и объектное программирование».
2.2.1 История
Идея структурного программирования зародилась в связи с сомнениями в целесообразности использования оператора goto.
Впервые эти сомнения высказал Хайнц Земанек в 1959 году, но они не получили тогда развития.
Ситуация изменилась в 1968 году, когда Эдсгер Дейкстра опубликовал статью «Оператор Go To считается вредным» (первоначально — «Доводы против оператора GO TO»). Этот документ оказал значительное влияние на развитие программирования.
С 1970-х годов оператор goto подвергался систематической критике. Его необдуманное использование приводит к созданию запутанного, нечитаемого «спагетти-кода», в котором сложно проследить порядок выполнения и взаимосвязи фрагментов. Дейкстра отметил, что качество кода обратно пропорционально количеству операторов goto, а код без goto проще проверять на корректность.
Проблемы использования goto:
Доводы оказались настолько весомыми, что goto стал рассматриваться как крайне нежелательный. Это отразилось в новых языках: например, goto запрещён в Java и Ruby, хотя в некоторых языках (как Ада) сохранён для редких случаев, где он оправдан по соображениям эффективности.
2.2.2 Цель
Цель структурного программирования — повысить производительность труда программистов, особенно при разработке больших и сложных программных комплексов. Оно направлено на сокращение числа ошибок, упрощение отладки, модификации и сопровождения программного обеспечения. Эта цель стала актуальной в 1960–1970-е годы из-за стремительного роста сложности программ и неспособности разработчиков управлять этой сложностью традиционными методами.
2.2.3 Спагетти-код
Структурное программирование призвано устранить беспорядок и ошибки, вызванные трудностью чтения и анализа неструктурированного кода, часто называемого «спагетти-код».
Спагетти-код — плохо спроектированная, слабо структурированная, запутанная и трудная для понимания программа, содержащая много операторов goto (особенно переходов назад), исключений и других конструкций, ухудшающих структурированность. Один из самых известных антипаттернов программирования.
Название отражает извилистый и запутанный ход выполнения, похожий на миску спагетти. Иногда такой код называют «кенгуру-код» из-за обилия инструкций прыжков (jump).
В настоящее время термин применяется не только к случаям злоупотребления goto, но и к любому «многосвязному» коду, в котором один и тот же небольшой фрагмент исполняется в большом количестве различных ситуаций и выполняет много различных логических функций.
Спагетти-код может быть отлажен и работать правильно и с высокой производительностью, но он крайне сложен в сопровождении и развитии. Доработка спагетти-кода для добавления новой функциональности иногда несет значительный потенциал внесения новых ошибок. По этой причине становится практически неизбежным рефакторинг — главное лекарство от спагетти.
2.2.4 Теорема о структурном программировании
Теорему сформулировали и доказали итальянские математики Коррадо Бём и Джузеппе Якопини (Giuseppe Jacopini). Они опубликовали её в 1965 году на итальянском языке и в 1966 году на английском.
Наряду с теоремой, в статье Бёма и Якопини описывались методы преобразования неструктурных алгоритмов в структурные на примере созданного Бёмом языка программирования P′′. Язык P′′ — первый полный по Тьюрингу язык программирования без оператора goto.
В современной терминологии теорема Бёма-Якопини утверждает:
Любая программа, заданная в виде блок-схемы, может быть представлена с помощью трёх управляющих структур:
• последовательность — обозначается: f THEN g, (сначала выполняется f, затем g)
• ветвление — обозначается: IF p THEN f ELSE g,
• цикл — обозначается: WHILE p DO f,
где f, g — блок-схемы с одним входом и одним выходом,
р — условие, THEN,
IF, ELSE, WHILE, DO — ключевые слова.
Пояснение. Формула f THEN g означает следующее: сначала выполняется программа f, затем выполняется программа g.
Данная теорема резко контрастирует с обычной (в 1960—1970 годы) практикой программирования, когда наблюдалось массовое использование операторов перехода goto.
Бём и Якопини не употребляли термин «структурное программирование». Тем не менее, доказанную ими теорему (и её последующие вариации у разных авторов) впоследствии стали называть «теоремой о структурном программировании», «структурной теоремой», «теоремой о структурировании».
2.2.5 Принципы структурного программирования
Становление и развитие структурного программирования связано с именем Эдсгера Дейкстры.
Основные принципы:
Принцип 1. Следует отказаться от использования оператора безусловного перехода goto.
Принцип 2. Любая программа строится из трёх базовых управляющих конструкций: последовательность, ветвление, цикл.
‒ Последовательность — однократное выполнение операций в том порядке, в котором они записаны в тексте программы.
‒ Ветвление — однократное выполнение одной из двух или более операций, в зависимости от выполнения заданного условия.
‒ Цикл — многократное исполнение одной и той же операции до тех пор, пока выполняется заданное условие (условие продолжения цикла).
Принцип 3. В программе базовые управляющие конструкции могут быть вложены друг в друга произвольным образом. Никаких других средств управления последовательностью выполнения операций не предусматривается.
Принцип 4. Повторяющиеся фрагменты программы можно оформить в виде подпрограмм (процедур и функций). Таким же образом (в виде подпрограмм) можно оформить логически целостные фрагменты программы, даже если они не повторяются.
В этом случае в тексте основной программы, вместо помещённого в подпрограмму фрагмента, вставляется инструкция «Вызов подпрограммы». При выполнении такой инструкции работает вызванная подпрограмма. После этого продолжается исполнение основной программы, начиная с инструкции, следующей за командой «Вызов подпрограммы».
Принцип 5. Каждую логически законченную группу инструкций следует оформить как блок. Блоки являются основой структурного программирования.
Блок — это логически сгруппированная часть исходного кода, например, набор инструкций, записанных подряд в исходном коде программы. Понятие блок означает, что к блоку инструкций следует обращаться как к единой инструкции.
Блоки служат для ограничения области видимости переменных и функций. Блоки могут быть пустыми или вложенными один в другой.
Границы блока строго определены. Например, в if-инструкции блок ограничен кодом BEGIN..END (в языке Паскаль) или фигурными скобками {…} (в языке C) или отступами (в языке Питон).
Принцип 6. Все перечисленные конструкции должны иметь один вход и один выход.
Произвольные управляющие конструкции (такие, как в блюде спагетти) могут иметь произвольное число входов и выходов. Ограничив себя управляющими конструкциями с одним входом и одним выходом, мы получаем возможность построения произвольных алгоритмов любой сложности с помощью простых и надежных механизмов.
Принцип 7. Разработка программы ведётся пошагово, методом «сверху вниз» (top-down method).
Задание 2. Основные принципы Дейкстры
Объясните своими словами следующие принципы структурного программирования и приведите по одному практическому примеру их применения:
Пример для анализа: Как можно заменить типичное использование goto для обработки ошибок в языке C структурными конструкциями?
Пример для анализа: Рассмотрите алгоритм бинарного поиска. Разбейте его на базовые конструкции и покажите, что дополнительные управляющие структуры не требуются.
Пример для анализа: Сравните функцию с несколькими return в разных местах и функцию с одним return в конце. Как это влияет на покрытие тестами?
Пример для анализа: Приведите пример фрагмента кода из 15 строк, который выполняет одну задачу (например, валидацию email) и объясните, почему его стоит выделить в отдельную функцию.
Пример для анализа: Опишите пошаговый процесс разработки программы "Калькулятор" методом сверху вниз, начиная с основного меню и постепенно детализируя операции.
Здание 3. Сравнение с другими парадигмами
Заполните таблицу, сравнивая структурное программирование с другими парадигмами:
|
Принцип/Характеристика |
Структурное программирование |
Объектно-ориентированное |
Функциональное |
|
Базовая единица программы |
|||
|
Управление состоянием |
|||
|
Основной механизм абстракции |
|||
|
Отношение к побочным эффектам |
|||
|
Подход к обработке ошибок |
|||
|
Критерии декомпозиции |
|||
|
Поддержка параллелизма |
|
Вопрос для размышления: Какие принципы структурного программирования оказались настолько фундаментальными, что были унаследованы всеми последующими парадигмами?
Распространены две методики (стратегии) разработки программ, относящиеся к структурному программированию:
– программирование «сверху вниз»;
– программирование «снизу вверх».
2.2.6 Метод «сверху вниз» (Top-Down)
Программирование «сверху вниз», или нисходящее программирование – это методика разработки программ, при которой разработка начинается с определения целей решения проблемы, после чего идет последовательная детализация, заканчивающаяся детальной программой.
Процесс:
1. Сначала пишется текст основной программы, в котором, вместо каждого связного логического фрагмента текста, вставляется вызов подпрограммы-заглушки, которая будет выполнять этот фрагмент.
Вместо настоящих, работающих подпрограмм, в программу вставляются фиктивные части — заглушки, которые, говоря упрощенно, ничего не делают.
2. Заглушка удовлетворяет требованиям интерфейса заменяемого фрагмента (модуля), но не выполняет его функций или выполняет их частично.
3. После проверки правильности общей структуры и последовательности вызовов, заглушки постепенно заменяются или дорабатываются до настоящих полнофункциональных фрагментов (модулей, подпрограмм) в соответствии с планом программирования.
На каждой стадии процесса реализации уже созданная программа должна правильно работать по отношению к более низкому уровню. Полученная программа проверяется и отлаживается.
4. Разработка заканчивается тогда, когда не останется ни одной заглушки.
Преимущества: такая последовательность гарантирует, что на каждом этапе разработки программист одновременно имеет дело с обозримым и понятным ему множеством фрагментов, и может быть уверен, что общая структура всех более высоких уровней программы верна.
При сопровождении и внесении изменений в программу выясняется, в какие именно процедуры нужно внести изменения. Они вносятся, не затрагивая части программы, непосредственно не связанные с ними. Это позволяет гарантировать, что при внесении изменений и исправлении ошибок не выйдет из строя какая-то часть программы, находящаяся в данный момент вне зоны внимания программиста.
Как отмечал Тони Хоар, принципы структурного программирования применимы как при разработке «сверху вниз», так и «снизу вверх».
2.2.7 Метод «снизу вверх»
Разработка программ «снизу вверх» (восходящее программирование) – это подход, при котором разработка начинается с создания небольших базовых алгоритмов (подпрограмм), выполняющих отдельные элементарные задачи, которые затем объединяются для создания более сложных алгоритмов и в конечном итоге для решения всей поставленной задачи
Подпрограммы бывают двух видов – процедуры и функции.
Процедура - выполняет группу операторов,
Функция - вычисляет некоторое значение и передает его обратно в главную программу.
Данные передаются подпрограмме в виде параметров или аргументов, которые обычно описываются в ее заголовке так же, как переменные.
Подпрограммы вызываются, как правило, путем простой записи их названия с нужными параметрами.
Подпрограммы могут быть вложенными – допускается вызов подпрограммы не только из главной программ, но и из любых других программ.
В некоторых языках программирования допускается вызов подпрограммы из себя самой, такой прием называется рекурсией и опасен тем, что может привести к зацикливанию – бесконечному самовызову.
. Преимущества метода:
‒ базовые алгоритмы можно многократно использовать в разных частях алгоритма (программы), что уменьшает его длину;
‒ алгоритм состоит из небольших модулей, что облегчает понимание и отладку;
‒ легко добавлять новые готовые базовые алгоритмы или изменять существующие, не затрагивая остальную часть основного алгоритма;
‒ базовые алгоритмы (подпрограммы) можно объединять в библиотеки и использовать в других проектах.
Недостатки метода:
‒ требуется чёткое понимание общей структуры задачи;
‒ сложность контроля прогресса;
‒ возможность отклонения от цели: легко увлечься разработкой отдельных модулей и забыть о главной цели.
Когда следует использовать метод «снизу вверх»:
‒ есть чёткое понимание базовых операций, необходимых для решения задачи;
‒ нужно создать библиотеку повторно используемых компонентов;
‒ задача предполагает много повторяющихся действий.
Задание 4: Сравните две методологии структурного программирования:
|
Критерий сравнения |
Программирование «сверху вниз» |
Программирование «снизу вверх» |
|
Основная идея |
||
|
Первый шаг разработки |
||
|
Процесс проектирования |
|
|
|
Преимущества |
||
|
Недостатки |
||
|
Когда предпочтительнее использовать |
|
2.8 Подпрограмма
Подпрограммы не являлись необходимым условием возможности реализации структурного программирования. но стали его важной частью.
Изначально подпрограммы появились как средство оптимизации программ по объёму занимаемой памяти – они позволили не повторять в программе идентичные блоки кода, а описывать их однократно и вызывать по мере необходимости.
К настоящему времени данная функция подпрограмм стала вспомогательной, главное их назначение – структуризация программы с целью удобства её понимания и сопровождения.
Выделение набора действий в подпрограмму и вызов её по мере необходимости позволяет:
2.9. Применение трёх базовых структур управления
В структурном программировании последовательность, ветвление и цикл — три базовые управляющие конструкции.
Эти три структуры обеспечивают создание любого алгоритма без необходимости прибегать к хаотичным переходам.
Блгоритм
Свойства алгоритма
Способы представления алгоритмов
Условные обозначения в алгоритмах
В соответствии с парадигмой, любая программа, которая строится без использования оператора goto, состоит из этих конструкций.
Особенность базовых структур — наличие у них одного входа и одного выхода. В программе базовые конструкции могут быть вложены друг в друга произвольным образом.
Последовательность
Последовательность — однократное выполнение операций в том порядке, в котором они записаны в тексте программы.

Некоторые особенности:



Привести примеры на java и других языках
Ветвление
Ветвление — однократное выполнение одной из двух или более операций в зависимости от выполнения заданного условия.
Условные операторы, такие как if, switch, позволяют выбирать между несколькими вариантами действий, основываясь на текущем состоянии данных.
Некоторые особенности:


Она содержит логический элемент проверки условия Р и два функциональных
блока S1 и S2. При исполнении алгоритма сначала вычисляется значение логи-
ческого выражения (проверяется условие), если оно истинно, то выполняется
блок S1, иначе (если ложно) блок S2. Такой вид развилки называется полной
условной конструкцией.




Структура развилка используется также в неполной форме. В этом случае,
если значение логического выражения ложно, никакое действие не выполняет-
ся. Такой вид развилки называется неполной условной конструкцией.
Структура неполной развилки имеет вид:





Цикл
Цикл — многократное исполнение одной и той же операции до тех пор, пока выполняется заданное условие (условие продолжения цикла).
Конструкции, такие как for, while, обеспечивают многократное выполнение одного и того же блока. Это позволяет сократить дублирование и автоматизировать обработку повторяющихся задач.
Некоторые типы циклов:
Структура цикла с предусловием состоит из логического элемента проверки условия Р и функционального блока S, называемого телом цикла. Она имеет
вид:

Цикл с предусловием выполняется так: сначала проверяется условие (отсюда название - цикл с предусловием), т.е. вычисляется значение логического выражения. Если оно истинно, то выполняется тело цикла, и снова проверяется условие. Выполнение цикла завершается, когда значение логического выражения становится ложным. Для этого необходимо, чтобы в теле цикла существовала команда, которая влияла бы на условие.
\


Структура цикла с постусловием также состоит из логического элемента
проверки условия Р и функционального блока S – тела цикла.

Цикл с постусловием выполняется так: сначала выполняется команда (ко-
манды) в теле цикла, затем проверяется условие, т.е. вычисляется значение ло-
гического выражения. Если оно ложно, то снова выполняются команды в теле
цикла, и так до тех пор, пока значение логического выражения не примет зна-
чение истина, после чего выполнение цикла завершается. Необходимо, чтобы в
теле цикла существовала команда, влияющая на условие.
Различие между циклами не только в том, что один с постусловием, а дру-
гой с предусловием, но и в том, что в цикле с предусловием функциональный
блок S может ни разу не выполниться, если условие Р при первой проверке
окажется ложным.
В цикле с постусловием функциональный блок всегда хотя
бы один раз выполнится






![]()


Преимущества и недостатки структурного программирования
Преимущества:
Читаемость, ясность. Такой подход упрощает восприятие кодирования, делает его более доступным для разработчиков. Явные конструкции, такие как циклы, условные операторы, облегчают понимание алгоритмов, что делает программу проще для чтения и модификации.
Упрощённое тестирование, отладка. Разбивка софта на отдельные модули, которые могут быть протестированы независимо. Это упрощает обнаружение и устранение ошибок, поскольку каждая часть проверяется отдельно. Модули, легко заменяемые и тестируемые, значительно снижают вероятность ошибок при изменении кода.
Снижение сложности. Использование базовых структур, таких как последовательности, ветвления, циклы, помогает сделать кодирование логически простым и организованным. Это значительно уменьшает вероятность возникновения путаницы в программе, облегчая её поддержку. Чёткая структура позволяет избежать хаоса в кодировании, а также способствует его лучшему восприятию.
Обучение, развитие начинающих программистов. Применение таких базовых принципов, как разделение задачи на модули, использование циклов и условий, помогает новичкам освоить важнейшие концепции и получить практические навыки, необходимые для более сложных подходов.
Повторное использование, модульность. Модульность позволяет создавать повторно используемые части, которые могут быть применены в других проектах. Это способствует сокращению времени разработки и повышению общей производительности.
Лёгкость в модификациях. Когда программа организована в виде модулей, изменения в одной части не влияют на остальные её компоненты. Это позволяет без значительных усилий обновлять или расширять систему, обеспечивая гибкость и удобство в поддержке и добавлении новых функциональных возможностей.
Недостатки
Масштабируемость в крупных проектах. На крупных проектах, где код становится значительно сложнее, можно столкнуться с трудностями. Управление зависимостями между модулями и их взаимодействие может усложниться. В таких случаях переход к другим парадигмам, например объектно-ориентированному подходу, может стать необходимым для упрощения разработки.
Ограниченная гибкость. Когда требуется внести значительные изменения в систему, эти корректировки могут стать сложными и неэффективными. Это связано с жесткостью структуры и сложностью интеграции новых функциональностей.
Трудности в поддержке крупных программ. Без должной документации и хорошей организации кода, изменения в структуре могут привести к ошибкам и увеличению сложности работы. Поддержка таких программ может потребовать дополнительных усилий для поддержания читаемости и согласованности кода.
Отсутствие сложных абстракций. Отсутствие классов и объектов затрудняет создание сложных абстракций, что приводит к дублированию кода и повторному написанию схожих алгоритмов для разных частей софта.
Невозможность полноценной многозадачности. Это может создавать проблемы при разработке многозадачных приложений или при работе с потоками данных.
Неэффективность при работе с динамическими данными. Когда софт должен работать с непредсказуемыми или изменяющимися данными, рассматриваемый подход может оказаться менее гибким и эффективным, чем другие парадигмы.
2.10 концепции структурного программирования в Java
В Java структурное программирование выражается через четкую организацию кода, основанную на последовательности, выборе и циклах, реализуемых в рамках структуры классов и методов. Его принципы тесно переплетены с объектно-ориентированной парадигмой языка.
|
Концепция |
Суть принципа |
Реализация в Java (как "структура") |
|
Линейная последовательность |
Код выполняется сверху вниз, инструкция за инструкцией. |
Порядок
операторов внутри метода |
|
Ветвление (выбор) |
Принятие решений в программе. |
Операторы |
|
Циклы (повторение) |
Многократное выполнение блока кода. |
Операторы |
|
Модульность / Нисходящее проектирование |
Разделение сложной задачи на подзадачи (модули). |
Методы: логически
обособленные блоки кода внутри класса |
|
Структурированные данные |
Организация данных для эффективного доступа и управления. |
Массивы: базовый тип |
|
Отказ от безусловных переходов (goto) |
Улучшение читаемости и предсказуемости кода. |
В
Java оператора |
В Java вы можете использовать те же самые операторы структурного программирования, что и языке программирования С, а именно:
|
Оператор |
Для чего применяется |
|
if, else |
Позволяет выбирать между двумя или более путями. Выполнение тех или иных строк программы в зависимости от выполнения логического условия, указанного в операторе if |
|
for |
Оператор цикла. Проверка условия завершения, а также итерация выполняется в начале цикла |
|
while |
Оператор цикла с проверкой условия завершения, выполнямой в начале цикла |
|
do-while |
Оператор цикла с проверкой условия завершения, выполнямой в конце цикла |
|
continue |
Продолжение выполнения цикла с его начала |
|
break |
Прерывание выполнения цикла |
|
switch |
Переключатель. Используется для исполнения того или иного фрагмента кода в зависимости от значения переменной или выражения |
В начале фрагмента программы, демонстрирующего применение оператора if-else, мы создаем три переменных:
int i = 5;int j = 6;boolean f;
Программа вычисляет выражение (i - j)*2 и сравнивает его с нулем в операторе if:
if((i - j) * 2 > 0) System.out.println("(" + i + " - " + j + ") * 2 > 0"); else{ System.out.println("(" + i + " - " + j + ") * 2 <= 0"); f = (i == i); if(f) { System.out.println("i == i"); }
}
В зависимости от знака выражения на консоли отображается одна из двух строк. В нашем случае выражение имеет отрицательный знак (оно равно -2), поэтому на консоль будет выведена следующая строка:
(5 - 6) * 2 <= 0
Второй оператор if выполняется только в том случае, если выражение (i - j)*2 меньше нуля. В этом операторе анализируется содержимое логической переменной f.
Если эта переменная равна true, то на консоль выводится текстовая строка, показанная ниже:
i == i
Переменная f вычисляется следующим образом:
f = (i == i);
Здесь в переменную f записывается результат операции сравнения переменной i самой с собой. Он всегда равен true.Оператор for
Цикл for в нашей программе выглядит следующим образом:
for(i = 0; i < 10; i++){ System.out.print(i + " "); }
Здесь переменная i используется в качестве переменной цикла. Перед началом цикла ей присваивается нулевое значение.
Затем содержимое i сравнивается с числом 10. Если i меньше 10, тело цикла выполняется один раз. Вслед за этим значение i увеличивается на единицу.
Далее i вновь сравнивается с числом 10. Когда i превысит это значение, работа цикла будет прервана.
Таким образом, параметр цикла анализируется перед выполнением тела цикла, а модифицируется после выполнения этого тела.
Вот что приведенный выше фрагмент программы выведет на консоль:
0 1 2 3 4 5 6 7 8 9
Оператор break может прервать выполнение цикла в любой момент. Например, в нашей программе мы прерываем работу цикла, когда значение переменной i становится больше пяти:
for(i = 0; i < 10; i++){ if(i > 5) break;System.out.print(i + " ");
}
В результате на консоль будут выведены цифры от 0 до 5:
0 1 2 3 4 5
Оператор while проверяет условие завершения цикла перед выполнением тела цикла:
i = 0;while(i < 10){ System.out.print(i + " "); i++;
}
В отличие от оператора for оператор while никак не изменяет значение переменной цикла, поэтому мы должны позаботиться об этом сами.
Перед тем как приступить к выполнению цикла, мы устанавливаем начальное значение параметра цикла i, равное нулю.
После выполнения тела цикла мы сами изменяем значение параметра цикла, увеличивая его не единицу.
Цикл будет прерван, как только значение переменной i превысит 10.
Ниже мы показали цикл, в котором используется оператор continue:
i = 0;while(true){ i++; System.out.print(i + " "); if(i < 9) continue; else break;}
Если в ходе выполнения цикла значение переменной i не достигло девяти, цикл возобновляет свою работу с самого начала (то есть с оператора i++).
Когда указанное значение будет достигнуто, выполнение цикла будет прервано оператором break.
При использовании оператора do-while условие завершения цикла проверяется после выполнения его тела:
i = 0;do{ System.out.print(i + " "); i++;
} while(i < 10)
В этом цикле мы сами устанавливаем начальное значение параметра цикла i и сами его изменяем, увеличивая на единицу.
Как только это значение достигнет 10, цикл будет прерван.
В нашей программе оператор switch используется для выбора одной из нескольких строк, отображаемых на консоли. Выбор делается в зависимости от значения переменной i:
i = 2; switch(i){ case 1: { System.out.println("case 1"); break; } case 2: { System.out.println("case 2"); break; } case 3: { System.out.println("case 3"); break; } default: { System.out.println("default"); break;
}}
Так как содержимое переменной i равно двум, на консоли всегда будет отображаться строка "case 2". Вы можете попробовать другие значения самостоятельно, отредактировав соответствующим образом исходный текст примера.
Контрольные вопросы:
Материалы на данной страницы взяты из открытых источников либо размещены пользователем в соответствии с договором-офертой сайта. Вы можете сообщить о нарушении.