Способ программирования при котором вся программа разбивается на группу компонентов
Модульное программирование – это такой способ
программирования, при котором вся программа
разбивается на группу компонентов, называемых модулями,
причем каждый из них имеет свой контролируемый размер,
четкое назначение и детально проработанный интерфейс с
внешней средой.
Альтернатива модульности – монолитная программа
3. Основные концепции:
В основе модульного программирования лежат три основные
концепции:
Принцип утаивания информации Парнаса
При разработке программ формируется список проектных решений,
которые особенно трудно понять или которые , скорее всего, будут
меняться. Затем определяются отдельные модули, каждый из
которых реализует одно из указанных решений.Большие программы
должны использовать модули без каких-либо предварительных
знаний об их внутренней структуре. Примерами удачных модулей
могут служить программы ППП (пакетов прикладных программ) и
стандартные процедуры.
Сборочное программирование Цейтина.
Модули – это программные «кирпичи», из которых строится
программа.
4. Основные концепции:
Аксиома модульности Коуэна
Модуль – независимая программная единица,
служащая для выполнения некоторой
определенной функции программы и для связи с
остальной частью программы. Программная
единица должна удовлетворять следующим
условиям:
Блочность организации
Синтаксическая обособленность
Семантическая независимость
Общность данных
Полнота определения
5. Характеристики модуля (Майерс, 1980)
Размер модуля
Связность (прочность) модуля
Сцепление модуля с другими модулями
Рутинность (независимость от предыдущих
обращений) модуля
6. Размер модуля
Модуль не должен превышать 60 строк
В результате его можно поместить на одну страницу
распечатки или легко просмотреть на экране монитора
7. Связность модуля
Связность модуля – мера независимости его
частей.
Чем выше связность, тем больше связей он
«упрятывает» в себе
Типы связности:
Функциональная
Модуль с функциональной связностью реализует одну какуюнибудь функцию и не может быть разбит на два модуля с теми
же типами связности
Последовательная
8. Связность модуля
Типы связности:
Последовательная
Модуль с такой связностью может быть разбит на
последовательные части, выполняющие независимые
функции, но реализующие совместно единственную функцию
(например, оценка , а затем обработка данных)
9. Связность модуля
Типы связности:
Информационная
Модуль с информационной связностью – это модуль,
выполняющий несколько операций или функций над одной и
той же структурой данных, которая считается неизвестной вне
этого модуля (применяется для реализации, например,
абстрактных типов данных таких как стек, очередь и др.)
10. Связность модуля: Следует избегать
Временной связности
- когда объединяются действия, связанные со временем
(например, действия, которые должны быть выполнены в один и
тот же момент времени)
Логической связности
- когда в модуль объединяются действия по признаку их
некоторого подобия (например, функции для проверки
корректности входных данных для всей программы)
Случайной связности
- когда действия объединяются произвольным образом
Процедурной связности
- когда действия сгруппированы вместе только потому, что они
выполняются в течение одной и той же части процесса
11. Сцепление модулей
Сцепление – мера относительной независимости
модулей от других модулей.
Независимые модули могут быть модифицированы без
переделки других модулей.
Чем слабее сцепление модуля, тем лучше.
12. Типы сцепления
Независимые модули – идеальный случай.
В этом случае модули ничего не знают друг о друге.
Взаимодействие модулей организуется через их
интерфейсы, когда выходные данные одного модуля
передаются на вход другого.
Достичь такого сцепления очень сложно, и в
большинстве случаев не нужно.
13. Типы сцепления
Сцепление по данным (параметрическое) – это
сцепление, когда данные передаются модулю как
значения его параметров или как результат его
обращения к другому модулю для вычисления некоторой
функции (Этот тип сцепления реализуется в языках
программирования при обращении к функциям)
Разновидности этого сцепления:
Сцепление по простым элементам данных
Сцепление по структуре данных (оба модуля при этом должны
знать о внутренней структуре данных)
14. Типы сцепления Не рекомендуется использовать:
Сцепление по управлению – это сцепление,
в котором один модуль управляет решениями внутри
другого с помощью передачи флагов, переключателей и
т.п.
В этом случае один модуль должен достаточно хорошо
знать структуру вызывающего модуля
Сцепление по внешним ссылкам – возникает, когда у
одного модуля есть доступ к данным другого
Сцепление по кодам – возникает, когда коды
инструкций модулей перемежаются друг с другом
(внутренняя область одного модуля доступна другому)
15. Рутинность модуля
Рутинность модуля – это независимость модуля от
предыдущих обращений к нему.
Будем называть модуль рутинным, если результат его
работы зависит только от количества переданных
параметров (а не от количества обращений)
16. Рутинность модуля
В некоторых случаях возникает необходимость в
создании модулей, которые должны сохранять
предысторию (не рутинные)
В выборе степени рутинности пользуются тремя
рекомендациями:
В большинстве случаев делаем модуль рутинным
Зависящие от предыстории модули следует использовать
только в том случае, когда необходимо сцепление по данным
В спецификации зависящего от предыстории модуля должна
быть сформулирована эта зависимость
17. Свойства модуля
На модуль можно ссылаться с помощью имени
модуля.
Модуль должен иметь один вход и один выход
Модуль должен быть сравнительно невелик
Возможность сепаратной компиляции
Модуль может вызвать другой модуль или сам себя
Модуль должен возвращать управление тому, кто
его вызвал
Модуль не должен сохранять историю своих
вызовов для управления своим функционированием
18. Преимущества модульного программирования:
Функциональные компоненты модульной
программы могут быть написаны и отлажены
порознь
Модульную программу проще проектировать,
легче сопровождать и модифицировать
Становится проще процедура загрузки в
оперативную память боль-шой программы,
требующей сегментации
19. Недостатки модульного программирования:
Может
увеличиться
время
компиляции
и
загрузки.
Может
увеличиться
время
исполнения
программы.
Может возрасти объем требуемой памяти.
Организация межмодульного взаимодействия
может оказаться довольно сложной.
Для современных компьютеров
недостатка несущественны.
первые
три
20. Стандартные модули
Разработка и использование стандартных
библиотечных программ является одним из путей
построения модульного программирования
Преимущества стандартных модулей:
1) стандартные модули экономят время
программирования;
2) они также могут экономить память компьютера и
выполняться максимально быстро;
3) использование стандартных модулей защищает от
ошибок программирования.
21. Стандартные модули
Недостатки:
Нужный стандартный модуль иногда бывает трудно найти. Еще
труднее – подробную документацию к нему
Стандартный модуль может оказаться более универсальным, чем это
нужно пользователю
Стандартный модуль может быть написан на другом языке
Каждый программист решает самостоятельно использовать ему
стандартные модули или разрабатывать свой собственный.
22. Подпрограммы (функции)
Подпрограммы также являются средством для
построения модульных программ
Не всякая подпрограмма является модулем.
Модуль должен удовлетворять перечисленным
выше характеристикам и свойствам.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Open with Desktop
- View raw
- Copy raw contents Copy raw contents
Copy raw contents
Copy raw contents
Язык программирования — это способ записи программ решения различных задач на компьютере в понятной для компьютера форме.
Процессор компьютера непосредственно понимает язык машинных команд.
Программы на таких языках программисты писали лишь для самых первых ламповых машин — ЭВМ первого поколения.
Программирование на языке машинных команд — дело непростое. Программист должен знать числовые коды всех машинных команд, должен сам распределять память под команды программы и данные.
Эволюция языков программирования
В 1950-х гг. появляются первые средства автоматизации программирования — языки Автокоды. Позднее для языков этого уровня стало применяться название «Ассемблеры».
Появление языков типа Ассемблер облегчило участь программистов.
Переменные величины стали изображаться символическими именами. Числовые коды операций заменились на мнемонические (словесные) обозначения, которые легче запомнить.
Язык программирования стал понятнее для человека, но при этом удалился от языка машинных команд.
Чтобы компьютер мог исполнять программы на Ассемблере, потребовался специальный переводчик — транслятор. Транслятор — это системная программа, переводящая текст программы на Ассемблере в текст эквивалентной программы на язык машинных команд.
Компьютер, оснащенный транслятором с Ассемблера, понимает Ассемблер. В этом случае можно говорить о псевдо-ЭВМ (аппаратура плюс транслятор с Ассемблера), языком которой является Ассемблер.
Языки типа Ассемблер являются машинно-ориентированными, т.е. они настроены на структуру машинных команд конкретного компьютера. Разные компьютеры с разными типами процессоров имеют разный Ассемблер.
Классификация языков программирования.
Язык машинных команд и ассемблер являются языками низкого уровня.
Язык низкого уровня – это язык программирования, предназначенный для определенного типа компьютера и отражающий его внутренний машинный код; языки низкого уровня часто называют машинно-ориентированными языками. Их сложно конвертировать для использования на компьютерах с разными центральными процессорами, а также довольно сложно изучать, поскольку для этого требуется хорошо знать внутренние принципы работы компьютера.
Помучавшись с языками низкого уровня человечество придумало языки высокого уровня.
Язык высокого уровня – это язык программирования, предназначенный для программиста; он не зависит от внутренних машинных кодов компьютера любого типа. Языки высокого уровня используют для решения проблем, и поэтому их часто называют проблемно-ориентированными языками. Каждая команда языка высокого уровня эквивалентна нескольким командам в машинных кодах, поэтому программы, написанные на языках высокого уровня, более компактны, чем аналогичные программы в машинных кодах.
Одна и та же программа на таком языке может быть выполнена на компьютерах разных типов, оснащенных соответствующим транслятором.
Форма записи программ на языках высокого уровня по сравнению с Ассемблером еще ближе к традиционной математической форме, к естественному языку. Языки высокого уровня легко изучаются, хорошо поддерживают структурную методику программирования.
Почитать для саморазвития.
Первыми популярными языками высокого уровня, появившимися в 1950-х гг., были Фортран, Кобол (в США) и Алгол (в Европе). Языки Фортран и Алгол были ориентированы на научно-технические расчеты математического характера. Кобол — язык для программирования экономических задач. В Коболе по сравнению с двумя другими названными языками слабее развиты математические средства, но зато хорошо развиты средства обработки текстов, организация вывода данных в форме требуемого документа. Для первых языков высокого уровня предметная ориентация языков была характерной чертой.
Большое количество языков программирования появилось в 1960—1970-х гг. А за всю историю ЭВМ их было создано более тысячи. Но распространились, выдержали испытание временем немногие. В 1965 г. в Дартмутском университете был разработан язык Бейсик. По замыслу авторов это простой язык, легко изучаемый, предназначенный для программирования несложных расчетных задач. Наибольшее распространение Бейсик получил на микроЭВМ и персональных компьютерах. На некоторых моделях школьных компьютеров программировать можно только на Бейсике.
Однако Бейсик — неструктурный язык, и потому он плохо подходит для обучения качественному программированию. Справедливости ради следует заметить, что последние версии Бейсика для ПК (например, QBasic) стали более структурными и по своим изобразительным возможностям приближаются к таким языкам, как Паскаль.
В эпоху ЭВМ третьего поколения получил большое распространение язык PL/1
Значительным событием в истории языков программирования стало создание в 1971 г. языка Паскаль. Его автор — швейцарский профессор Никлаус Вирт — разрабатывал Паскаль как учебный язык структурного программирования.
Наибольший успех в распространении этого языка обеспечили персональные компьютеры. Фирма Borland International, Inc (США) разработала систему программирования Турбо Паскаль для ПК (позднее, с добавлением поддержки ООП, язык стал называться Object Pascal, а интегрированная система программирования - Delphi).
Паскаль — это не только язык и транслятор с него, но еще и операционная оболочка, обеспечивающая пользователю удобство работы. Паскаль вышел за рамки учебного предназначения и стал языком профессионального программирования с универсальными возможностями. Транслятор с Паскаля по оптимальности создаваемых им программ близок наиболее удачному в этом отношении транслятору — транслятору с Фортрана. В силу названных достоинств Паскаль стал основой нескольких других языков программирования, например, таких как Ада, Модула-2 и др.
Несмотря на хороший старт, в настоящее время Паскаль сдал свои позиции как язык коммерческой разработки. Его еще использую как учебный в некоторых вузах и техникумах, но даже тут он постепенно уступает свои позиции, как мало востребованный на рынке.
Причин этому, на мой взгляд, несколько: и плохая ценовая политика (дистрибутив Дельфи не по карману начинающим разработчикам), так и переход программистского сообщества на открытые языки (open source), во множестве появившиеся в последнее время.
Язык программирования Си (английское название — С) создавался как инструментальный язык для разработки операционных систем, трансляторов, баз данных и других системных и прикладных программ. Так же как и Паскаль, Си — это язык структурного программирования, но, в отличие от Паскаля, в нем заложены возможности непосредственного обращения к некоторым машинным командам, к определенным участкам памяти компьютера. Дальнейшее развитие Си привело к созданию языка объектно-ориентированного программирования Си++.
Еще немного для саморазвития.
Модула-2 — это еще один язык, предложенный Н.Виртом, основанный на языке Паскаль и содержащий средства для создания больших программ.
Компьютеры будущего, пятого поколения называют машинами «искусственного интеллекта». Но прототипы языков для этих машин были созданы существенно раньше их физического появления. Это языки ЛИСП и Пролог.
ЛИСП появился в 1965 г. Язык ЛИСП основан на понятии рекурсивно определенных функций. А поскольку доказано, что любой алгоритм может быть описан с помощью некоторого набора рекурсивных функций, то ЛИСП, по сути, является универсальным языком. С его помощью на компьютере можно моделировать достаточно сложные процессы, в частности интеллектуальную деятельность людей.
Язык Пролог разработан во Франции в 1972 г. также для решения проблемы «искусственного интеллекта». Пролог позволяет в формальном виде описывать различные утверждения, логику рассуждений и заставляет компьютер давать ответы на заданные вопросы.
Реализовать тот или иной язык программирования на ЭВМ — это значит создать транслятор с этого языка для данной ЭВМ (тут имеется в виду не конкретный экземпляр ЭВМ, а архитектура процессора и поддерживаемая им система команд).
Существуют два принципиально различных метода трансляции. Они называются соответственно компиляция и интерпретация. Для объяснения их различия можно предложить следующую аналогию: лектор должен выступить перед аудиторией на незнакомом ей языке.
Перевод можно организовать двумя способами:
- полный предварительный перевод — лектор заранее передает текст выступления переводчику, тот записывает перевод, размножает его и раздает слушателям (после чего лектор может и не выступать);
- синхронный перевод — лектор читает доклад, переводчик одновременно с ним слово в слово переводит выступление.
Компиляция является аналогом полного предварительного перевода; интерпретация — аналогом синхронного перевода. Транслятор, работающий по принципу компиляции, называется компилятором; транслятор, работающий методом интерпретации, — интерпретатором.
При компиляции в память компьютера загружается программа-компилятор. Она воспринимает текст программы на языке высокого уровня как исходную информацию. После завершения компиляции получается программа на языке машинных команд. Затем в памяти остается только программа на языке машинных команд, которая выполняется, и получаются требуемые результаты.
Интерпретатор в течение всего времени работы программы находится во внутренней памяти. В ОЗУ помещается и программа на языке высокого уровня. Интерпретатор в последовательности выполнения алгоритма «читает» очередной оператор программы, переводит его в команды и тут же выполняет эти команды. Затем переходит к переводу и выполнению следующего оператора. При этом результаты предыдущих переводов в памяти не сохраняются. При повторном выполнении одной и той же команды она снова будет транслироваться. При компиляции исполнение программы разбивается на два этапа: трансляцию и выполнение. При интерпретации, поскольку трансляция и выполнение совмещены, программа на ЭВМ проходит в один этап. Однако откомпилированная программа выполняется быстрее, чем интерпретируемая.
Все это было востребовано в эпоху господства Wintel (Windows + Intel), т.е. одна ОС, одна платформа (Apple и сейчас остается нишевым продуктом), но в последние годы значительную часть рынка отвоевал Android (около 40% на 2018 год) и постепенно набирает популярность Linux, а на рынке процессоров теперь господствует архитектура ARM.
Прикладным программам стала требоваться кроссплатформенность (возможность работать под любой ОС и на любой платформе) и на фоне возросшей мощности компьютерного "железа" востребованы стали интерпретируемые языки, т.к. для обеспечения их кроссплатформенности "достаточно" сделать интерпретатор языка под нужную ОС и платформу.
А теперь посмотрим на TOP-5 самых востребованых языков программирования (критерии оценки могут быть разными, как и получившийся список, но эти чаще всего встречаются):
Также в современной разработке скоростью выполнения программы пренебрегают в пользу скорости разработки. Порог вхождения в разработку в этих языках в целом намного ниже, чем в Си или Паскаль.
Например, на PHP плюются все разработчики, но из-за чрезвычаной простоты языка сайты, разработанные на нем занимают до 80%.
Понятие системы программирования
Системы программирования - это комплекс инструментальных программных средств, предназначенный для разработки программ на одном или нескольких языках программирования. Системы программирования предоставляют сервисные возможности программистам для разработки их собственных компьютерных программ.
Современные системы программирования обычно предоставляют пользователям мощные и удобные средства разработки программ. В них входят:
- компилятор или интерпретатор;
- интегрированная среда разработки;
- средства создания и редактирования текстов программ;
- обширные библиотеки стандартных программ и функций;
- отладочные программы, т.е. программы, помогающие находить и устранять ошибки в программе;
- "дружественная" к пользователю диалоговая среда;
- многооконный режим работы;
- мощные графические библиотеки; утилиты для работы с библиотеками
- встроенный ассемблер;
- встроенная справочная служба;
- другие специфические особенности.
Многие системы программирования включают также средства RAD (RapidApplication Development – быстрая разработка приложений), например, простой и удобный способ разработки графического интерфейса.
Классификация систем программирования
По набору входных языков различают системы программирования одно- и многоязыковые. Отличительная черта многоязыковых систем состоит в том, что отдельные части программы можно составлять на разных языках и с помощью специальных обрабатывающих программ объединять их в готовую для исполнения на ЭВМ программу.
По структуре, уровню формализации входного языка и целевому назначению различают системы программирования машинно-ориентированные и машинно-независимые. Машинно-ориентированные системы программирования имеют входной язык, наборы операторов и изобразительные средства которых существенно зависят от особенностей ЭВМ (внутреннего языка, структуры памяти и т.д.). Машинно-ориентированные системы позволяют использовать все возможности и особенности машинно-зависимых языков:
- высокое качество создаваемых программ;
- возможность использования конкретных аппаратных ресурсов;
- предсказуемость объектного кода и заказов памяти;
- для составления эффективных программ необходимо знать систему команд и особенности функционирования данной ЭВМ;
- трудоемкость процесса составления программ (особенно на машинных языках и ЯСК), плохо защищенного от появления ошибок;
- низкая скорость программирования;
- невозможность непосредственного использования программ, составленных на этих языках, на ЭВМ других типов.
Отладчик в системе программирования
Отладка — этап разработки компьютерной программы, на котором обнаруживают, локализуют и устраняют ошибки. Чтобы понять, где возникла ошибка, приходится:
- узнавать текущие значения переменных;
- выяснять, по какому пути выполнялась программа.
Существуют две взаимодополняющие технологии отладки.
- Использование отладчиков — программ, которые включают в себя пользовательский интерфейс для пошагового выполнения программы: оператор за оператором, функция за функцией, с остановками на некоторых строках исходного кода или при достижении определённого условия.
- Вывод текущего состояния программы с помощью расположенных в критических точках программы операторов вывода — на экран, принтер, громкоговоритель или в файл. Вывод отладочных сведений в файл называется журналированием.
Отладчик помогает анализировать поведение отлаживаемой программы, обеспечивая ее трассировку, выполняя остановы в указанных точках или при заданных условиях, позволяя просмотреть текущие значения переменных, содержимое ячеек памяти, а иногда и регистров процессора, и при необходимости изменить эти значения. Отладчик является важной составной частью системы программирования.
Трассировка программы заключается в выполнении программы или ее участка, сопровождающая выводом на экран, принтер или другой регистрацией в хронологической последовательности информации о событиях, связанных с выполнением программы. Трассировка программы применяется при отладке или тестировании программы, когда программа пользователя или ее отлаживаемый участок выполняется под управлением специальной программы-трассировщика. При этом, например, можно выводить на экран или в некоторый регистрирующий файл все встретившиеся при выполнении программы помеченные операторы в той последовательности, в которой они фактически выполняются. Таким образом может отслеживаться программная логика. При трассировке программы можно контролировать и значения переменных, важных для поиска ошибки, и т.д. Информация об отладочных действиях задается трассировщику, который изменяет объектную программу в оперативной памяти, размещая в точках трассировки команды перехода на программу трассировки, выполняющую требуемую регистрацию. Более сложные трассировщики могут также изменять объектную программу,исключать и вставлять операторы, позволяя таким образом программисту в течение одного и того же прогона программы не только локализовать ошибки, но и попытаться исключить их и посмотреть на результат этого исключения.
Популярные системы программирования:
Хочу обратить внимание на Visual Studio Code — активно развивающийся, бесплатный редактор исходного кода, разработанный Microsoft для Windows, Linux и macOS. Позиционируется как «лёгкий» редактор кода для кроссплатформенной разработки веб- и облачных приложений. Включает в себя отладчик, инструменты для работы с Git, подсветку синтаксиса, IntelliSense и средства для рефакторинга. Т.е. это не полноценный IDE, но вполне подходит для разработки на PHP, JS, имеет систему плагинов, т.е. может быть добавлена поддержка и других языков.
Исходный, объектный и загрузочный модули.
Рассмотрим структуру абстрактной многоязыковой, открытой, компилирующей системы программирования и процесс разработки приложений в данной среде.
Современные системы программирования позволяют удобно переходить от одного этапа к другому. Это осуществляется в рамках так называемой интегрированной среды программирования, которая содержит в себе текстовый редактор, компилятор, компоновщик, встроенный отладчик и, в зависимости от системы или ее версии, предоставляет программисту дополнительные удобства для написания и отладки программ.
Интегрированная среда программирования.
Устанавливать нужно только "Классические приложения .NET"
Самое общеизвестное определение структурного программирования – подход к программированию, в котором для передачи управления в программе используется три конструкции: следование, выбора и цикл.
Классическая теорема Боэма и Джакопини о структурном программировании утверждает, что всякую правильную программу (т. е. программу с одним входом и одним выходом, без зацикливаний и недостижимых веток) можно записать с использованием следующих логических структур:
последовательности двух или более операторов;
Дейкстра предложил отказаться от оператора безусловного перехода и ограничиться тремя конструкциями – последовательностью, выбором и циклом;
Дональд Кнут продемонстрировал случаи, в которых оператор безусловного перехода оказывался полезным (например, выход из нескольких вложенных циклов) и подверг критике утверждение Дейкстры.
В 1965 году академик Глушков обратил внимание на то, что структурированные программы можно рассматривать как формулы в некоторой алгебре. Зная правила преобразования выражений в такой алгебре, можно осуществлять глубокие формальные (и, следовательно, автоматизированные) преобразования программ.
Структурное программирование – не самоцель, его основное назначение – получение хорошей программы. Однако даже в самой хорошей программе операторы перехода требуются, например при выходе из множества вложенных циклов.
Модульное программирование
Модульное программирование – это такой способ программирования, при котором вся программа разбивается на группу компонентов, называемых модулями, причем каждый из них имеет свой контролируемый размер, четкое назначение и детально проработанный интерфейс с внешней средой. Единственная альтернатива модульности – монолитная программа, что, конечно, неудобно. Таким образом, наиболее интересный вопрос при изучении модульности – определение критерия разбиения на модули. В основе модульного программирования лежат три основные концепции.
Принцип утаивания информации. Всякий компонент утаивает единственное проектное решение, т. е. модуль служит для утаивания информации. Подход к разработке программ заключается в том, что сначала формируется список проектных решений, которые особенно трудно принять или которые, скорее всего, будут меняться. Затем определяются отдельные модули, каждый из которых реализует одно из указанных решений.
Аксиома модульности. Модуль – независимая программная единица, служащая для выполнения некоторой определенной функции программы и для связи с остальной частью программы. Программная единица должна удовлетворять следующим условиям:
– блочность организации, т. е. возможность вызвать программную единицу из блоков любой степени вложенности;
– синтаксическая обособленность, т. е. выделение модуля в тексте синтаксическими элементами;
– семантическая независимость, т. е. независимость от места, где программная единица вызвана;
– общность данных, т. е. наличие собственных данных, сохраняющихся при каждом обращении;
– полнота определения, т. е. самостоятельность программной единицы.
Сборочное программирование. Модули – это программные кирпичи, из которых строится программа.
Сцепление модулей – мера относительной независимости модуля от других модулей. Независимые модули могут быть модифицированы без переделки других модулей. Чем слабее сцепление модуля, тем лучше. Рассмотрим различные типы сцепления.
– независимые модули – это идеальный случай. Модули ничего не знают друг о друге. Организовать взаимодействие таких модулей можно, зная их интерфейс и соответствующим образом перенаправив выходные данные одного модуля на вход другого.
– сцепление по данным (параметрическое) - это сцепление, когда данные передаются модулю, как значения его параметров, либо как результат его обращения к другому модулю для вычисления некоторой функции. Этот вид сцепления реализуется в языках программирования при обращении к функциям (процедурам).
Рутинность модуля – это независимость модуля от предыдущих обращений к нему (от предыстории). Модуль является рутинным, если результат его работы зависит только от количества переданных параметров (а не от количества обращений). Модуль должен быть рутинным в большинстве случаев, но есть и случаи, когда модуль должен сохранять историю. В выборе степени рутинности модуля пользуются тремя рекомендациями:
– в большинстве случаев делаем модуль рутинным;
– зависящие от предыстории модули следует использовать только в тех случаях, когда это необходимо для сцепления по данным;
– в спецификации зависящего от предыстории модуля должна быть четко сформулирована эта зависимость, чтобы пользователи имели возможность прогнозировать поведение такого модуля.
Вычислительная модель. Если под вычислителем понимать современный компьютер, то его состоянием будут значения всех ячеек памяти, состояние процессора (в том числе — указатель текущей команды) и всех сопряженных устройств. Единственная структура данных — последовательность ячеек (пар «адрес» - «значение») с линейно упорядоченными адресами.
В качестве математической модели императивное программирование использует машину Тьюринга-Поста — абстрактное вычислительное устройство, предложенное на заре компьютерной эры для описания алгоритмов.
Синтаксис и семантика. Языки, поддерживающие данную вычислительную модель, являются как бы средством описания функции переходов между состояниями вычислителя. Основным их синтаксическим понятием является оператор. Первая группа — простые операторы, у которых никакая их часть не является самостоятельным оператором (например, оператор присваивания, оператор безусловного перехода, вызова процедуры и т. п.). Вторая группа — структурные операторы, объединяющие другие операторы в новый, более крупный оператор (например, составной оператор, операторы выбора, цикла и т. п.).
Традиционное средство структурирования — подпрограмма (процедура или функция). Подпрограммы имеют параметры и локальные определения и могут быть вызваны рекурсивно. Функции возвращают значения как результат своей работы.
Если в данной методологии требуется решить некоторую задачу для того, чтобы использовать ее результаты при решении следующей задачи, то типичный подход будет таким. Сначала исполняется алгоритм, решающий первую задачу. Результаты его работы сохраняются в специальном месте памяти, которое известно следующему алгоритму, и используются им.
Императивные языки программирования.Императивные языки программирования манипулируют данными в пошаговом режиме, используя последовательные инструкции и применяя их к разнообразным данным. Считается, что первым алгоритмическим языком программирования был язык Plankalkuel (от plan calculus), разработанный в 1945—1946 годах Конрадом Цузе (Konrad Zuse).
Большинствои из наиболее известных и распространенных императивных языков программирования было создано в конце 50-х — середине 70-х годов XX века. Это период 80-х и 90-х годов соответствует увлечениям новыми парадигмами, и императивных языков в это время практически не появлялось.
Класс задач. Императивное программирование наиболее пригодно для решения задач, в которых последовательное исполнение каких-либо команд является естественным. Примером здесь может служить управление современными аппаратными средствами. Поскольку практически все современные компьютеры императивны, эта методология позволяет порождать достаточно эффективный исполняемый код. С ростом сложности задачи императивные программы становятся все менее и менее читаемыми.
Программирование и отладка действительно больших программ (например, компиляторов), написанных исключительно на основе методологии императивного программирования, может затянуться на долгие годы.
Рекомендации по литературе. Особенности императивного программирования изложены в огромном количестве книг. Наиболее систематично они приведены в работе "Универсальные языки программирования. Семантический подход" [Калинин, Мацкевич 1991].
2.1. Модульное программирование.
Модульное программирование — это такой способ программирования, при котором вся программа разбивается на группу компонентов, называемых модулями, причем каждый из них имеет свой контролируемый размер, четкое назначение и детально проработанный интерфейс с внешней средой. Единственная альтернатива модульности — монолитная программа, что, конечно, неудобно. Таким образом, наиболее интересный вопрос при изучении модульности — определение критерия разбиения на модули.
Концепции модульного программирования.В основе модульного программирования лежат три основных концепции:
Принцип утаивания информации Парнаса. Всякий компонент утаивает единственное проектное решение, т. е. модуль служит для утаивания информации. Подход к разработке программ заключается в том, что сначала формируется список проектных решений, которые особенно трудно принять или которые, скорее всего, будут меняться. Затем определяются отдельные модули, каждый из которых реализует одно из указанных решений.
Аксиома модульности Коуэна. Модуль — независимая программная единица, служащая для выполнения некоторой определенной функции программы и для связи с остальной частью программы. Программная единица должна удовлетворять следующим условиям:
· блочность организации, т. е. возможность вызвать программную единицу из блоков любой степени вложенности;
· синтаксическая обособленность, т. е. выделение модуля в тексте синтаксическими элементами;
· семантическая независимость, т. е. независимость от места, где программная единица вызвана;
· общность данных, т. е. наличие собственных данных, сохраняющихся при каждом обращении;
· полнота определения, т. е. самостоятельность программной единицы.
Сборочное программирование Цейтина. Модули — это программные кирпичи, из которых строится программа. Существуют три основные предпосылки к модульному программированию:
· стремление к выделению независимой единицы программного знания. В идеальном случае всякая идея (алгоритм) должна быть оформлена в виде модуля;
· потребность организационного расчленения крупных разработок;
· возможность параллельного исполнения модулей (в контексте параллельного программирования).
Определения модуля и его примеры.Приведем несколько дополнительных определений модуля.
· Модуль — это совокупность команд, к которым можно обратиться по имени.
· Модуль — это совокупность операторов программы, имеющая граничные элементы и идентификатор (возможно агрегатный).
Функциональная спецификация модуля должна включать:
· синтаксическую спецификацию его входов, которая должна позволять построить на используемом языке программирования синтаксически правильное обращение к нему;
- описание семантики функций, выполняемых модулем по каждому из его входов.
Разновидности модулей.Существуют три основные разновидности модулей:
1) "Маленькие" (функциональные) модули, реализующие, как правило, одну какую-либо определенную функцию. Основным и простейшим модулем практически во всех языках программирования является процедура или функция.
2) "Средние" (информационные) модули, реализующие, как правило, несколько операций или функций над одной и той же структурой данных (информационным объектом), которая считается неизвестной вне этого модуля. Примеры "средних" модулей в языках программирования:
a)задачи в языке программирования Ada;
b)кластер в языке программирования CLU;
c)классы в языках программирования C++ и Java.
3) "Большие” (логические) модули, объединяющие набор "средних" или "маленьких" модулей. Примеры "больших" модулей в языках программирования:
a)модуль в языке программирования Modula-2;
b)пакеты в языках программирования Ada и Java.
Набор характеристик модуля предложен Майерсом [Майерс 1980]. Он состоит из следующих конструктивных характеристик:
1) размера модуля;
В модуле должно быть 7 (+/-2) конструкций (например, операторов для функций или функций для пакета). Это число берется на основе представлений психологов о среднем оперативном буфере памяти человека. Символьные образы в человеческом мозгу объединяются в "чанки" — наборы фактов и связей между ними, запоминаемые и извлекаемые как единое целое. В каждый момент времени человек может обрабатывать не более 7 чанков.
Модуль (функция) не должен превышать 60 строк. В результате его можно поместить на одну страницу распечатки или легко просмотреть на экране монитора.
2) прочности (связности) модуля;
Существует гипотеза о глобальных данных, утверждающая, что глобальные данные вредны и опасны. Идея глобальных данных дискредитирует себя так же, как и идея оператора безусловного перехода goto. Локальность данных дает возможность легко читать и понимать модули, а также легко удалять их из программы.
Связность (прочность) модуля (cohesion) — мера независимости его частей. Чем выше связность модуля — тем лучше, тем больше связей по отношению к оставшейся части программы он упрятывает в себе. Можно выделить типы связности, приведенные ниже.
Функциональная связность. Модуль с функциональной связностью реализует одну какую-либо определенную функцию и не может быть разбит на 2 модуля с теми же типами связностей.
Последовательная связность. Модуль с такой связностью может быть разбит на последовательные части, выполняющие независимые функции, но совместно реализующие единственную функцию. Например, один и тот же модуль может быть использован сначала для оценки, а затем для обработки данных.
Информационная (коммуникативная) связность. Модуль с информационной связностью — это модуль, который выполняет несколько операций или функций над одной и той же структурой данных (информационным объектом), которая считается неизвестной вне этого модуля. Эта информационная связность применяется для реализации абстрактных типов данных.
Обратим внимание на то, что средства для задания информационно прочных модулей отсутствовали в ранних языках программирования (например, FORTRAN и даже в оригинальной версии языка Pascal). И только позже, в языке программирования Ada, появился пакет — средство задания информационно прочного модуля.
3) сцепления модуля с другими модулями;
Сцепление (coupling) — мера относительной независимости модуля от других модулей. Независимые модули могут быть модифицированы без переделки других модулей. Чем слабее сцепление модуля, тем лучше. Рассмотрим различные типы сцепления.
Независимые модули — это идеальный случай. Модули ничего не знают друг о друге. Организовать взаимодействие таких модулей можно, зная их интерфейс и соответствующим образом перенаправив выходные данные одного модуля на вход другого. Достичь такого сцепления сложно, да и не нужно, поскольку сцепление по данным (параметрическое сцепление) является достаточно хорошим.
Сцепление по данным (параметрическое) — это сцепление, когда данные передаются модулю, как значения его параметров, либо как результат его обращения к другому модулю для вычисления некоторой функции. Этот вид сцепления реализуется в языках программирования при обращении к функциям (процедурам). Две разновидности этого сцепления определяются характером данным.
· Сцепление по простым элементам данных.
· Сцепление по структуре данных. В этом случае оба модуля должны знать о внутренней структуре данных.
4) рутинности (идемпотентность, независимость от предыдущих обращений) модуля.
Рутинность — это независимость модуля от предыдущих обращений к нему (от предыстории). Будем называть модуль рутинным, если результат его работы зависит только от количества переданных параметров (а не от количества обращений).
Модуль должен быть рутинным в большинстве случаев, но есть и случаи, когда модуль должен сохранять историю. В выборе степени рутинности модуля пользуются тремя рекомендациями.
· В большинстве случаев делаем модуль рутинным, т. е. независимым от предыдущих обращений.
· Зависящие от предыстории модули следует использовать только в тех случаях, когда это необходимо для сцепления по данным.
- В спецификации зависящего от предыстории модуля должна быть четко сформулирована эта зависимость, чтобы пользователи имели возможность прогнозировать поведение такого модуля.
2.2. Структурное программирование.
Структурное программирование (СП) возникло как вариант решения проблемы уменьшения СЛОЖНОСТИ разработки программного обеспечения.
В начале эры программирования работа программиста ничем не регламентировалась. Решаемые задачи не отличались размахом и масштабностью, использовались в основном машинно-ориентированные языки и близкие к ним язык типа Ассемблера, разрабатываемые программы редко достигали значительных размеров, не ставились жесткие ограничения на время их разработки.
По мере развития программирования появились задачи, для решения которых определялись ограниченные сроки все более сложных задач с привлечением групп программистов. И как следствие, разработчики столкнулись с тем, что методы, пригодные для разработки небольших задач, не могут быть использованы при разработке больших проектов в силу сложности последних.
Таким образом, цель структурного программирования - повышение надежности программ, обеспечение сопровождения и модификации, облегчение и ускорение разработки.
Методология структурного императивного программирования — подход, заключающийся в задании хорошей топологии императивных программ, в том числе отказе от использования глобальных данных и оператора безусловного перехода, разработке модулей с сильной связностью и обеспечении их независимости от других модулей.
Подход базируется на двух основных принципах:
· Последовательная декомпозиция алгоритма решения задачи сверху вниз.
· Использование структурного кодирования.
Напомним, что данная методология является важнейшим развитием императивной методологии.
Происхождение, история и эволюция. Создателем структурного подхода считается Эдсгер Дейкстра. Ему также принадлежит попытка (к сожалению, совершенно неприменимая для массового программирования) соединить структурное программирование с методами доказательства правильности создаваемых программ. В его разработке участвовали такие известные ученые как Х. Милс, Д.Э. Кнут, С. Хоор.
Методы и концепции, лежащие в основе структурного программирования. Их три
Метод алгоритмической декомпозиции сверху вниз — заключается в пошаговой детализации постановки задачи, начиная с наиболее общей задачи. Данный метод обеспечивает хорошую структурированность. Метод поддерживается концепцией алгоритма.
Метод модульной организации частей программы — заключается в разбиении программы на специальные компоненты, называемые модулями. Метод поддерживается концепцией модуля.
Метод структурного кодирования — заключается в использовании при кодировании трех основных управляющих конструкций. Метки и оператор безусловного перехода являются трудно отслеживаемыми связями, без которых мы хотим обойтись. Метод поддерживается концепцией управления
Структурные языки программирования. Основное отличие от классической методологии императивного программирования заключается в отказе (точнее, той или иной степени отказа) от оператора безусловного перехода.
[Пратт Т., 1979] "Важным для программиста свойством синтаксиса является возможность отразить в структуре программы структуру лежащего в ее основе алгоритма. При использовании для построения программы метода, известного под названием структурное программирование, программа конструируется иерархически - сверху вниз (от главной программы к подпрограммам самого нижнего уровня), с употреблением на каждом уровне только ограниченного набора управляющих структур: простых последовательностей инструкций, циклов и некоторых видов условных разветвлений. При последовательном проведении этого метода структуру результирующих алгоритмов легко понимать, отлаживать и модифицировать. В идеале у нас должна появиться возможность перевести построенную таким образом схему программы прямо в соответствующие программные инструкции, отражающие структуру алгоритма."
Теорема о структурировании (Бёма-Джакопини (Boem-Jacopini)): Всякую правильную программу (т.е. программу с одним входом и одним выходом без зацикливаний и недостижимых веток) можно записать с использованием следующих логических структур - последовательность, выбора и повторение цикла
Следствие 1: Всякую программу можно привести к форме без оператора goto.
Следствие 2: Любой алгоритм можно реализовать в языке, основанном на трех управляющих конструкциях -последовательность, цикл, повторение.
Следствие 3: Сложность структурированных программ ограничена, даже в случае их неограниченного размера.
Структурное программирование- это не самоцель. Его основное назначение- это получение хорошей ("правильной") программы, однако даже в самой хорошей программе операторы перехода goto иногда нужны: например - выход из множества вложенных циклов.
Практически на всех языках, поддерживающих императивную методологию, можно разрабатывать программы и по данной методологии. В ряде языков введены специальные заменители оператора goto, позволяющие облегчить управление циклами (например, Break и Continue в языке C).
Класс задач. Класс задач для данной методологии соответствует классу задач для императивной методологии. Заметим, что при этом удается разрабатывать более сложные программы, поскольку их легко воспринимать и анализировать.
Рекомендации по литературе. Одной из наиболее известных работ в этой области является статья "Заметки по структурному программированию" [Дейкстра 1975]. Методы структурного программирования подробно рассмотрены в книге "Теория и практика структурного прграммирования" [Лингер, Миллс, Уитт 1982]. Практику структурного программирования можно изучать по книге "Алгоритмы + структуры данных = программы" [Вирт 1985]. Философия визуального структурного программирования подробно изложена в работе [Паронджанов 1999].
Императивное программирование – это исторически первая методология программирования, которой пользовался каждый программист, программирующий на любом из «массовых» языков программирования – Бейсик, Паскаль, Си. Она ориентирована на классическую фон Неймановскую модель, остававшуюся долгое время единственной аппаратной архитектурой. Методология императивного программирования характеризуется принципом последовательного изменения состояния вычислителя пошаговым образом.
Императивное программирование наиболее пригодно для решения задач, в которых последовательное исполнение каких-либо команд является естественным. Примером здесь может служить управление современными аппаратными средствами. Поскольку практически все современные компьютеры императивны, эта методология позволяет порождать достаточно эффективный исполняемый код.
Однако с ростом сложности задач программы становятся все более объёмными и менее читаемыми. Поэтому программирование и отладка больших программ, написанных исключительно на основе методологии императивного программирования, может затянуться на долгие годы.
Структурное программирование, являясь развитием императивной методологии, зиждется на двух основных принципах: последовательная декомпозиция алгоритма решения задачи и использование структурного кодирования. Идея структурного программирования заключается в том, что структура программы должна отражать структуру решаемой задачи, чтобы алгоритм решения был ясно виден из текста программы.
Последовательная декомпозиция алгоритма решения задачи представляет собой так называемое нисходящее проектирование («сверху вниз») и предполагает разложение общей функции обработки данных на более простые функциональные элементы, подзадачи, каждая из которых также разбивается на более детальные до тех пор, пока алгоритм решения всей задачи не будет реализован в терминах языка программирования.
Структурное кодирование заключается в использовании только трех основных управляющих алгоритмических конструкций, каждая из которых имеет по одному входу и выходу, и их суперпозиции. Оператор безусловного перехода (GOTO) в структурном программировании не используется, так как он порождает трудно отслеживаемые при отладке связи.
С целью уменьшения сложности разработки программного обеспечения в технологии структурного программирования вводится понятие подпрограммы (программы в программе) – набора операторов, выполняющих нужное действие и не зависящих от других частей программы. Подпрограммы бывают двух видов – процедуры и функции. Процедура выполняет группу операторов (над некоторыми операндами), а функция, кроме того, возвращает (то есть передаёт обратно) в точку вызова вычисленное значение определённого типа.
Данные передаются подпрограмме в виде параметров или аргументов. Параметры, которые указываются в тексте заголовка, называются формальными. Они нужны только для формального описания тела подпрограммы. При выполнении программы, в момент вызова подпрограммы, подставляются конкретные значения (или ссылки на значения – переменные) – фактические параметры (см. , в котором происходит слияние двух строк "qwe" и "asd"). Приведём код подпрограмм (модуль «Пример6_ПроцедураФункция»), вызов которых используется в этом примере:
Sub proc1()
'Вызов процедуры AcB с параметрами s, "qwe", "asd"
Call AсB(s, "qwe", "asd")
End Sub
'Объявление процедуры AсB с параметрами c$, a$, b$
Sub AсB(c$, a$, b$)
End Sub
'Объявление функции AiB с параметрами a$, b$
Function AiB(a$, b$) As String
End Function
Sub proc2()
'Вызов функции AiB с параметрами "qwe", "asd"
End Sub
Необходимость разработки больших программных систем привела к появлению модульного программирования. Это такой способ программирования, при котором вся программа (точнее, проект) разбивается на составные части, называемые модулями, причем каждый из них имеет свой контролируемый размер, четкое назначение и детально проработанный интерфейс с внешней средой. Единственная альтернатива модульности – монолитная программа, конечно же, может быть удобна только при решении достаточно простых задач.
Концепция модульного программирования является основой всех современных подходов к проектированию и реализации программных систем. В то же время суть ее проста и отражает широко известные научные и технические методы, заключающиеся в поиске и реализации некоторого базового набора элементов, комбинации которых дают решение всех задач из определенного круга.
С применением модульного программирования появляются возможности коллективной разработки программ как набора «независимых» частей, последовательного уменьшения сложности методом разбиения сложной задачи на более простые подзадачи и, наконец, возможности повторного использования созданного ранее кода, в том числе применение восходящего («снизу вверх») проектирования.
Если концепция структурного программирования предлагает некоторый универсальный алгоритмический базис, то модульное программирование состоит в разработке под конкретную задачу или круг задач (предметную область) собственного базиса в виде набора модулей, позволяющего наиболее эффективно по целому ряду критериев построить программный комплекс. Модули, входящие в базис, это целые программы (в отличие от «макрооператоров» структурного программирования – подпрограмм), решающие некоторые подзадачи и часто оформляемые в виде отдельных файлов, причём так называемые модули расширения, могут быть написаны на совершенно другом языке.
12.3 Рекурсивные алгоритмы *
Рекурсия – это одна из фундаментальных концепций в математике и программировании. Это одна из форм мышления, это мощное средство, позволяющее строить элегантные и выразительные алгоритмы. Объект называется рекурсивным, если он содержит сам себя или определен с помощью самого себя.
Если процедура р содержит явное обращение к самой себе, то она называется явно рекурсивной. Если процедура р содержит обращение к некоторой процедуре q, которая в свою очередь содержит прямое или косвенное обращение к р, то р называется косвенно рекурсивной.
Рекурсивная программа не может вызывать себя бесконечно, иначе она никогда не остановится, таким образом в программе (функции) должен присутствовать еще один важный элемент – так называемое терминальное условие, то есть условие при котором программа прекращает рекурсивный процесс.
Рекуррентность – это рекурсивное определение функции. Классический пример такого рода функций – факториал. Напомним, факториал нуля равен 1, а факториал натурального числа N определяется как произведение натуральных чисел от единицы до N, что выражается рекуррентной формулой: N!=N (N-1)!, для N>=1 и 0! = 1. То есть для определения факториала одного числа требуется знать или вычислить факториал другого, уменьшенного на единицу. А это, в свою очередь, может потребовать определения факториала ещё меньшего числа. И так далее, до единицы. Этому напрямую соответствует нижеследующая рекурсивная функция:
Function factorial(N As Integer) As Long
If N=0 Then factorial=1 Else factorial=N*factorial(N-1)
End Function
Многие алгоритмы можно легко реализовать с помощью рекурсивных программ, и многие разработчики алгоритмов предпочитают выражать алгоритмы рекурсивно. Но делать это нужно крайне осторожно. Например, вызов factorial(-1) приведет к бесконечному рекурсивному циклу (аварийная остановка, конечно, будет, связанная с переполнением стека или выходом значения аргумента за пределы диапазона). Поэтому перед вызовом данной функции нужно делать проверку условия неотрицательности аргумента.
Стоит также отметить, что не все языки допускают использование рекурсий. В таких случаях можно обойтись использованием обычных циклов. Например, ту же функцию факториал реализовать без использования рекурсии:
Читайте также: