Что такое модульное программирование и кому оно нужно. «Забытые» парадигмы программирования Примеры модульного программирования

Модульное программирование работает по принципу «разделяй и властвуй». Стоит разобраться.

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

Сам термин «модуль» начал использоваться в программировании в связи с внедрением модульного принципа при написании программ. В семидесятых годах модулем называли какую-то функцию или процедуру, которая была написана по определенным правилам. Так как на тот момент не было сформировано общепризнанных требований, то модулем называли любую процедуру, чей размер составлял до пятидесяти строк. Парнасом были сформированы первые конкретизированные требования к модулю: « Для формирования одного модуля должно быть достаточно самых минимальных знаний о содержании другого». Получается, именно Парнас впервые сформировал концепцию скрытия информации в программировании. Его определение приводит нас к тому, что модулем может называться любая отдельная процедура как самого нижнего, так и самого верхнего уровня иерархии. Надежно скрытие информации нельзя было обеспечить посредством использования конструкций, существующих в то время, так как они было подвержены сильному действию глобальных переменных, а их поведение в сложных программах предсказать весьма сложно. Необходимо было создать конструкцию, изолированную от этих переменных. Именно ее и назвали модулем, а на его основе и зародилось модульное программирование.

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

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

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

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

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

Классическая проблема программирования

В западной литературе существует термин «big ball of mud» для описания архитектуры программы. Давайте переведём его дословно. Графически «большой шар грязи» можно представить в виде точек на окружности, символизирующих функциональные элементы, и прямых – связей между ними:

Похоже на ваши глаза перед сдачей проекта, не так ли?

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

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

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

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

  • Ускорение разработки.
  • Повышение надёжности.
  • Упрощение тестирования.
  • Взаимозаменяемость.

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

Но не всё так просто.

Проблемы модульного программирования

Сама по себе идея использования модулей не сильно упрощает код, важно минимизировать количество прямых связей между ними. Здесь мы подходим к понятию «инверсия управления» (IoC). Упрощённо – это принцип программирования, при котором отдельные компоненты кода максимально изолированы друг от друга. То есть детали одного модуля не должны влиять на реализацию другого. Достигается это при помощи интерфейсов или других видов представления, не обеспечивающих прямого доступа к модульному коду.

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

В модульном программировании существует три основные реализации:

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

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

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

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

  1. Обеспечить чёткое функциональное разделение кода. При возникновении ошибок можно быстро определить источник, а исправления не приведут к появлению новых сбоев.
  2. Минимизировать количество связей. Это позволит упростить разработку, отдав на откуп нескольким разработчикам разные модули. Или вы сможете самостоятельно разрабатывать каждый блок без оглядки на другие, что тоже экономит время и силы.
  3. Создать иерархию с чёткой вертикалью наследования. Это повышает надёжность кода, так как тестирование провести проще, а результаты информативнее.

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

Парадигмы программирования

Мо́дульное программи́рование - это организация программы как совокупности небольших независимых блоков, называемых модулями, структура и поведение которых подчиняются определённым правилам. Использование модульного программирования позволяет упростить тестирование программы и обнаружение ошибок. Аппаратно-зависимые подзадачи могут быть строго отделены от других подзадач, что улучшает мобильность создаваемых программ.

Мо́дуль - функционально законченный фрагмент программы . Во многих языках (но далеко не обязательно) оформляется в виде отдельного файла с исходным кодом или поименованной непрерывной её части. Некоторые языки предусматривают объединение модулей в пакеты .

Энциклопедичный YouTube

  • 1 / 5

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

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

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

    Модульная система модулей

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

    Реализация Лероя сама построена посредством языка модулей ML , а именно в виде функтора , параметризованного данными о ядре языка и описанием его механизма проверки согласования типов . Это значит, что при написании компилятора некоторого языка достаточно описать ядро языка и передать его данному функтору (как библиотечной функции) - в результате получится компилятор расширения известного языка системой модулей ML .

    История концепции модулей

    История концепции модулей как единиц компиляции восходит к языкам Фортран II и Кобол , то есть, к концу 1950-х годов . В 1976 году появилась публикация, в которой была развита концепция модульности - о языке Mesa (англ. ) , который был разработан в Xerox PARC . В 1977 году подробно ознакомился с этой концепцией учёный Никлаус Вирт , общаясь с разработчиками в Xerox PARC. Эти идеи были использованы Виртом при создании языка Модула-2 , публикация о котором вышла в 1977 году .

    Термин «модуль» в программировании начал использоваться в связи с внедрением модульных принципов при создании программ. В 1970-х годах под модулем понимали какую-либо процедуру или функцию, написанную в соответствии с определёнными правилами. Например: «модуль должен быть простым, замкнутым (независимым), обозримым (от 50 до 100 строк), реализующим только одну функцию задачи, имеющим одну входную и одну выходную точку».

    Первым основные свойства программного модуля более-менее чётко сформулировал Д. Парнас (David Parnas) в 1972 году : «Для написания одного модуля должно быть достаточно минимальных знаний о тексте другого». Таким образом, в соответствии с определением, модулем могла быть любая отдельная процедура (функция) как самого нижнего уровня иерархии (уровня реализации), так и самого верхнего уровня, на котором происходят только вызовы других процедур-модулей.

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

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

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

    Впервые специализированная синтаксическая конструкция модуля была предложена Н. Виртом в 1975 г. и включена в его новый язык Modula . Насколько сильно изменяются свойства языка, при введении механизма модулей, свидетельствует следующее замечание Н.Вирта, сделанное им по поводу более позднего языка Модула-2: «Модули - самая важная черта, отличающая язык Модула-2 от его предшественника Паскаля».

    Реализация в языках программирования

    Языки, формально поддерживающие концепцию модулей: IBM S/360

    Для уменьшения сложности программной системы она разбивается на множество небольших, в высокой степени независимых модулей. Модуль - это замкнутая программа, которую можно вызвать из любого другого модуля системы. Это фрагмент программного текста, являющийся строительным блоком для физической структуры системы. Как правило, модуль состоит из интерфейсной части и части-реализации. Модули можно разрабатывать на различных языках программирования и отдельно компилировать. Высокой степени независимости модулей программной системы можно достичь с помощью двух методов оптимизации: усилением внутренних связей в каждом модуле и ослаблением взаимосвязи между ними. Модульное программирование возникло в начале 60-х гг. XX в. . При создании программных систем оно дает следующие преимущества:

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

    Наряду с этими преимуществами имеются и некоторые недостатки, которые могут привести к возрастанию стоимости программной системы:

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

    Перечислим основные свойства и требования к модулям .

    • 1. Модуль возникает в результате сепаратной компиляции или является частью результата совместной компиляции. Он может активизироваться операционной системой или быть подпрограммой, вызываемой другим модулем.
    • 2. На содержимое модуля можно ссылаться с помощью его имени.
    • 3. Модуль должен возвращать управление тому, кто его вызвал.
    • 4. Модуль может обращаться к другим модулям.
    • 5. Модуль должен иметь один вход и один выход. Иногда программа с несколькими входами может оказаться короче и занимать меньше места в памяти. Однако опыт модульного программирования показал, что разработчики предпочитают иметь несколько похожих модулей, но не использовать несколько входов и выходов в одном модуле. Это объясняется тем, что единственность входа и выхода гарантирует замкнутость модуля и упрощает сопровождение программной системы.
    • 6. Модуль сравнительно невелик. Размеры модуля влияют на степень независимости элементов программы, легкость ее чтения и тестирования. Обнаружено, что небольшие модули позволяют строить такие программы, которые легче изменять. Такие модули чаще используются, они облегчают оценку и управление разработкой, их можно рекомендовать и достаточно опытным, и неопытным программистам. Можно было бы удовлетворить критериям высокой прочности и минимального сцепления, спроектировав программу как несколько больших модулей, но вряд ли таким образом была бы достигнута высокая степень независимости. Как правило, модуль должен содержать от 10 до 100 операторов языка высокого уровня (в некоторых публикациях - до 200).

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

    • 7. Модуль не должен сохранять историю своих вызовов для управления своим функционированием. Такой модуль называют предсказуемым. Модуль, хранящий следы своих состояний при последовательных вызовах, не является предсказуемым. Все модули ПС должны быть предсказуемыми, т.е. не должны сохранять никакой информации о предыдущем вызове. Хитрые, неуловимые, зависящие от времени ошибки возникают в тех программах, которые пытаются многократно вызвать непредсказуемый модуль.
    • 8. Структура принятия решения в модуле должна быть организована таким образом, чтобы те модули, на которые прямо влияет принятое решение, были подчиненными (вызываемыми) по отношению к принимающему решение модулю. Таким образом, обычно удается исключить передачу специальных параметров-индикаторов, представляющих решения, которые должны быть приняты, а также принимать влияющие на управление программой решения на высоком уровне в иерархии программы.
    • 9. Минимизация доступа к данным. Объем данных, на которые модуль может ссылаться, должен быть сведен к минимуму. Исключение сцепления по общей области, внешним данным и по формату - хороший шаг в этом направлении. Проектировщик должен попытаться изолировать сведения о любой конкретной структуре данных или записи в базе данных в отдельном модуле (или небольшом подмножестве модулей) -возможно, за счет использования информационно прочных модулей.
    • 10. Внутренние процедуры. Внутренняя процедура, или подпрограмма, - это замкнутая подпрограмма, физически расположенная в вызывающем ее модуле. Таких процедур следует избегать по нескольким причинам. Внутренние процедуры трудно изолировать для тестирования, и они не могут быть вызваны из модулей, отличных от тех, которые их содержат. Это не соответствует идее повторного использования. Конечно, имеется альтернативный вариант - включить копии внутренней процедуры во все модули, которым она нужна. Однако это часто приводит к ошибкам (копии часто становятся «не совсем точными») и усложняет сопровождение программы, поскольку, когда процедура изменяется, все модули нужно перекомпилировать.

    Программный модуль -- это отдельная часть программного кода, фрагмент программы, который полностью самостоятельно выполняет свою задачу, оформленный в виде отдельного файла с исходным кодом или поименованной непрерывной её части. Эти модули предназначаются для использования в других программах или же для расширения возможностей как, например, библиотека плагинов. Разбиение всей программы на отдельные модули позволяет решение общей, сложной задачи разделить на решение нескольких, более простых. Что в свою очередь увеличивает скорость и качество разработки, когда каждый занимается своим модулем программы, а затем их собирают в единый проект. В общем случае модули проектируются таким образом, чтобы предоставлять программистам удобную для многократного использования функциональность (интерфейс) в виде набора функций, классов, констант. Модули можно объединять в пакеты, а несколько пакетов можно объединять в библиотеки. Возможность изменения или обновления одного модуля не затрагивая всей программы и остальных модулей является главным удобством использования модульной архитектуры. Также разделение программы на модули позволяет распределить нагрузку, в случае большой потребности ПО в ресурсах, при запуске разных модулей на разных серверах, используя так называемую распределенную архитектуру.

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

    В основе использования модульной архитектуры лежат три основных концепции:

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

    Также имеются дополнительные определения программного модуля.

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

    Функциональная спецификация модуля должна включать:

    • - Описание синтаксической спецификации параметров, подаваемых на его вход, которое позволит, используя текущий язык программирования, правильный доступ к его функциональным возможностям;
    • - Описание, что делает каждая функция в модуле на каждый входной параметр.

    Модули также разделяют на три категории:

    • - «Маленькие» (функциональные) модули. Они, обычно, реализуют одну или несколько функций. Во всех языках программирования высокого уровня имеется поддержка основного и простейшего модуля: процедуры и функции.
    • - «Средние» (информационные) модули, реализующие, в обычном случае, сравнительно небольшое количество операций или функций над так называемым информационным объектом, который считается за пределами модуля неизвестным. Наиболее известные примеры информационных модулей это классы в таких языках программирования как С, С++, С#, Java и другие.
    • - «Большие» (логические) модули, которые объединяют в себе некоторое количество как «маленьких» так и «средних» модулей. Примером "больших" модулей в языках программирования могут послужить пакеты или сборки в таких языках программирования, как Java.
Loading...Loading...