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

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

Заметим, что нужно различать использование слова "модуль", когда имеется в виду синтаксическая конструкция языков программирования (unit в Object Pascal), и когда имеется в виду единица дробления большой программы на отдельные блоки (которые могут быть реализованы и в виде процедур, и в виде функций).

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

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

Термин "модуль" в программировании начал использоваться в связи с внедрением модульных принципов при создании программ. В 70-х годах под модулем понимали какую-либо процедуру или функцию, написанную в соответствии с определенными правилами. Например "Модуль должен быть простым, замкнутым (независимым), обозримым (от 50 до 100 строк), реализующим только одну функцию задачи, имеющим только одну входную и только одну выходную точку". Однако общепризнанных требований не было и модулем очень часто называли любую процедуру размером до 50 строк.

Первым основные свойства программного модуля более-менее четко сформулировал Парнас (Parnas): "Для написания одного модуля должно быть достаточно минимальных знаний о тексте другого". Таким образом! Парнас первым выдвинул концепцию скрытия информации (information! hiding) в программировании.

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

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

Впервые специализированная синтаксическая конструкция модуля была предложена Н.Виртом в 1975 г. и включена в его новый язык Modula. В этом же году была сделана опытная реализация языка Modula. После некоторой переработки этот новый язык был окончательно реализован в 1977 г. и получил название Modula-2. Впоследствии, аналогичные конструкции, с некоторыми отличиями, были включены и в другие языки программирования: Pascal Plus (Уэлш и Бастард, 1979 г.), Ada (1980), Turbo Pascal версии 4.0 и другие.

Изначально предполагалось, что при реализации сложных программных комплексов модуль должен использоваться наравне с процедурами и функциями как конструкция, объединяющая и надежно скрывающая детали реализации определенной подзадачи. Однако в языке Borland (Turbo) Pascal были реализованы не все теоретические возможности модуля. В частности, в этом языке отсутствует поддержка внутренних модулей (аналогичных внутренним процедурам и функциям), недостаточно гибко реализован импорт (предложение uses), который не позволяет импортировать объекты из других модулей выборочно. Это обстоятельство, а также то, что с появлением персональных компьютеров круг программирующих людей резко расширился (а это существенно снизило средний уровень теоретической подготовки программистов), привело к тому, что при разработке приложений на этой, предшествующей Object Pascal, версии языка модули использовались в основном как средство создания проблемных библиотек процедур и функций. И только наиболее квалифицированные программисты использовали всю мощь этой языковой конструкции для структурирования своих проектов. В языке Object Pascal отмеченные ограничения реализации модулей остались, однако благодаря тому, что в среде Delphi каждой форме обязательно соответствует свой модуль и не визуальные алгоритмические действия также, как правило, оформляются в виде отдельных модулей, конструкцию "модуль" стали использовать по ее первоначальному предназначению все программисты, независимо от их квалификации.

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

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

Рассмотрим еще один теоретический вопрос, связанный с использованием модульного программирования. Этот вопрос касается формы модульного проекта.

Придание иерархической структуре модульного проекта хорошей формы позволяет улучшить процесс ее разработки. Число модулей, которые подключаются каким-либо модулем, и число модулей, которые его подключают, оказывают влияние на сложность проекта. Йордан (Yourdon) назвал число модулей, подключаемых из данного модуля, размахом или шириной управления модулями. Наряду с большим размером модуля очень маленькая или очень большая ширина управления является признаком плохой схемы разбивки на модули. В общем случае, ширина управления модуля не должна превышать 10-ти. Это число связано с "магическим" числом 7, которое базируется на положениях психологии, в особенности, на теории "кусков" ("chunking") информации. Кратковременная память человека имеет ограниченные способности сохранения "кусков" информации. Психологические эксперименты показали, способность нашей кратковременной памяти находится в пределах 5-11 "кусков" (в среднем - 7). Она может одновременно оперировать около 7 "кусками" информации. Когда человек превышает этот предел, он более склонен к ошибкам. Реорганизация информации с разбивкой на подходящие части является важным действием для эффективного использования кратковременной памяти человека и для улучшения понимаемого материала. Люди во многих жизненных ситуациях делают такую реорганизацию бессознательно. Однако программист может сам себе помочь, сознательно не допуская ширины управления модулями, которая существенно превышает число 7.

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

Кроме того, эти же принципы следует применять и при проектировании иерархии классов. Например, в иерархии предопределенных классов Object Pascal, только два класса, TObject и Exception, имеют число непосредственных классов-потомков, значительно большее, чем 7. Это можно объяснить глобальной базовой ролью TObject и "перечисляемым" характером класса Exception. Еще у троих классов это число находится в пределах 8-9, а остальные классы имеют менее 7 непосредственных потомков.

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

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

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

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

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

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

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

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

Цели урока:

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

ХОД УРОКА

1. Проверка качества усвоения материала, изученного на предыдущих занятиях.

1.1. Устный опрос:

  • Какой вид имеет структура описания процедуры и функции в Turbo Pascal?
  • В чем состоит отличие описания процедуры и функции?
  • Что такое область действия идентификаторов?
  • Какие параметры называются формальными и какие фактическими?
  • Какие способы передачи параметров реализованы в Turbo Pascal?
  • Почему при работе с графикой в Turbo Pascal необходимо предложение uses Graph?
  • Какие процедуры и функции модуля Graph вам известны?

2. Изложение нового материала.

2.1.1. Технология модульного программирования.

Языки высокого уровня появились в 60-е годы. Ресурсы ЭВМ (объем ОЗУ 8 Кбайт, быстродействие 20 тыс. операций в сек.) были недостаточны, поэтому программисты вынуждены были писать программы весьма “хитроумно” с использованием оператора безусловного перехода. Программа получалась запутанной, имела структуру “блюдо спагетти”. Так как область применения ЭВМ расширялась, программное обеспечение усложнялось. Программисты, решающие сложные задачи, столкнулись с проблемой разрастания количества и размера программ до такой степени, что дальнейший процесс разработки становился практически неуправляемым, и никто из разработчиков не мог с уверенностью сказать, что созданный программный продукт всегда выполняет то, что требуется, и что он не выполняет ничего такого, что не требуется. Поэтому возникла необходимость в новой методологии разработки программных проектов. В 1968–1969 гг. состоялись конференции по программированию. На второй из них Эдсгер Дийкстра предложил принципиально новый способ создания прграмм – структурное программирование. Главное – разбиение программного комплекса (при его создании) на программные модули, которые соединяются иерархически.

Цели модульного программирования:

1. Улучшать читабельность программ.
2. Повышать эффективность и надежность программ (легко находить и корректировать ошибки).
3. Уменьшать время и стоимость программной разработки (уменьшается время отладки).

Разбиение программного комплекса на модули выполняется в соответствии со следующими принципами:

  1. Модуль – это независимый блок, код которого физически и логически отделен от кода других модулей.
  2. Размер модуля не больше 100 операторов.
  3. Имеет одну входную и одну выходную точку.
  4. Модули связаны иерархически.
  5. Разбиение должно обеспечивать надежное скрытие информации в модуле.
    Парнас: “Для написания одного модуля должно быть достаточно минимальных знаний о тексте другого”.
  6. Каждый модуль должен начинаться с комментария (его назначение – входные и выходные переменные).
  7. Не использовать метки и оператор GOTO.
  8. Использовать только стандартные управляющие конструкции (условие, выбор, цикл, блок).

2.1.2. Нисходящее и восходящее программирование .

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

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

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

Недостатки нисходящего проектирования:

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

Преимущество нисходящего проектирования – на самом начальном этапе проектирования отлаживается головной модуль (логика программы).

Преимущество восходящего программирования – не нужно писать заглушки.

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

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

2.1.3. Оформление программы в виде модуля.

При подключении стандартных модулей достаточно корректно записать их идентификаторы в предложении uses. При разработке собственных модулей необходимо помнить некоторые особенности:

  • Не допускается одновременное использование модулей с одинаковыми именами.
  • Идентификатор модуля, указанный в заголовке (unit ), должен совпадать с именами файлов, содержащих исходный (.pas ) (.tpu , . tpp , .tpw ) код.
  • Если идентификатор модуля длиннее восьми символов, то он должен совпадать с именами файлов по первым восьми символам.

Общая структура модуля

unit идентификатор модуля;

{Интерфейсный раздел }

interface

в этом разделе описывается взаимодействие данного модуля с другими пользовательскими и стандартными модулями, а также с главной программой. Другими словами – взаимодействие модуля с “внешним миром”.

Список импорта интерфейсного раздела

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

Список экспорта интерфейсного раздела

const
type
var
procedure
function

Раздел реализации

Implementation

в этом разделе указывается реализационная (личная) часть описаний данного модуля, которая недоступна для других модулей и программ. Другими словами – “внутренняя кухня модуля”.

Список импорта раздела реализации

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

Подразделы внутренних для модуля описаний

label
const
type
var
procedure
function

Раздел инициализации

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

2.2. Примеры.

Пример модуля a1

unit a1;
interface
uses graph;
procedure init;
procedure pr1;
implementation
procedure init;
{инициализация графического режима}
procedure pr1;
begin
{команды построения фрагмента изображения}
end;
begin
init;
pr1;
readln;
end.

Головной модуль

program a;
uses a1, a2, a3, a4;
begin
pr1;
pr2;
pr3;
pr4;
readln;
end.

3. Проверка качества усвоения нового материала.

3.1. Устный опрос .

  • Назовите принципы модульного программирования.
  • Когда применяется технология нисходящего программирования? А восходящего?
  • В чем различие между технологией восходящего и технологией нисходящего программирования?
  • Какие существуют особенности при разработке собственных модулей?
  • Из каких разделов состоит модуль?
  • Что описывается в разделе interface?
  • Что описывается в разделе implementation?
  • Что описывается в разделе инициализации?

3.2. Самостоятельная работа учащихся на уроке .

Учащиеся разбиты на две группы. Работой каждой группы руководит “начальник”. Получив задание, учащиеся начинают коллективную работу. Каждый ученик разрабатывает свою программу, оформляет ее в виде модуля UNIT и отдает “начальнику”, который пишет головную программу, объединяя модули своих “подчиненных”.

Литература:

  1. Марченко А. И., Марченко Л. М. “Программирование в среде Turbo Pascal 7.0”, М.: “Бином Универсал”, 1998.
  2. Информатика. № 2. /Приложение к газете “Первое сентября”, 1996.

Структурное программирование

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

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

последовательности двух или более операторов;

дихотомического выбора;

повторения;

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

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

В 1965 году академик Глушков обратил внимание на то, что структурированные программы можно рассматривать как формулы в некоторой алгебре. Зная правила преобразования выражений в такой алгебре, можно осуществлять глубокие формальные (и, следовательно, автоматизированные) преобразования программ.

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

Модульное программирование

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

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

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


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

– синтаксическая обособленность, т. е. выделение модуля в тексте синтаксическими элементами;

– семантическая независимость, т. е. независимость от места, где программная единица вызвана;

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

– полнота определения, т. е. самостоятельность программной единицы.

Сборочное программирование . Модули – это программные кирпичи, из которых строится программа.

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

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

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

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

– в большинстве случаев делаем модуль рутинным;

– зависящие от предыстории модули следует использовать только в тех случаях, когда это необходимо для сцепления по данным;

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

Назначение модулей

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

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

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

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

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

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

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

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

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

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

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

По своей организации и характеру использования в программе модули Турбо Паскаля близки к модулям-пакетам (PACKAGE) языка программирования Ада. В них так же, как и в пакетах Ады, явным образом выделяется некоторая «видимая» интерфейсная часть, в которой сконцентрированы описания глобальных типов, констант, переменных, а также приводятся заголовки процедур и функций. Появление объектов в интерфейсной части делает их доступными для других модулей и основной программы. Тела процедур и функций располагаются в исполняемой части модуля, которая может быть скрыта от пользователя.

Рис.2. Последовательность разработки программного проекта

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

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

Структура модулей

Всякий модуль имеет следующую структуру:

Unit <имя_модуля>;

i nterface

<интерфейсная часть>;

implementation

<исполняемая часть>;

<инициирующая часть>;

Здесь UNIT – зарезервированное слово (единица); начинает заголовок модуля;

<имя_модуля> - имя модуля (правильный идентификатор);

INTERFACE – зарезервированное слово (интерфейс); начинает интерфейсную часть модуля;

IMPLEMENTATION – зарезервированное слово (выполнение); начинает исполняемую часть модуля;

BEGIN – зарезервированное слово; начинает инициирующую часть модуля; причем конструкция begin <инициирующая часть> необязательна;

END – зарезервированное слово – признак конца модуля.

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

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

Unit ИдентификаторМодуля;

{Интерфейсный раздел }

interface

{В этом разделе описывается взаимодействие данного модуля} {с другими пользовательскими и стандартными модулями, а также} {с главной программой. Другими словами – взаимодействие } {модуля с «внешним миром». }

{Список импорта интерфейсного раздела }

{В этом списке через запятые перечисляются идентификаторы} {модулей, информация интерфейсных частей которых должна }

{быть доступна в данном модуле. Здесь целесообразно описывать} {идентификаторы только тех модулей, информация из которых} {используется в описаниях раздела interface данного модуля.}

{Список экспорта интерфейсного раздела }

const {Список экспорта состоит из подразделов описания констант,} type {типов, переменных, заголовков процедур и функций, которые} var {определены в данном модуле, но использовать которые разре-} procedure {шено во всех других модулях и программах, включающих имя} function {данного модуля в своей строке uses. Для процедур и функций} {здесь описываются только заголовки, но с обязательным} {полным описанием формальных параметров.}

{Раздел реализации }

implementation

{В этом разделе указывается реализационная (личная) часть} {описаний данного модуля, которая недоступна для других } {модулей и программ. Другими словами – «внутренняя кухня»}

{Список импорта раздела реализации }

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

{Подразделы внутренних для модуля описаний }

label {В этих подразделах описываются метки, константы, типы,} const {переменные, процедуры и функции, которые описывают} type {алгоритмические действия, выполняемые данным модулем, и} var {которые являются «личной собственностью» исключительно} procedure{только данного модуля. Эти описания недоступны ни одному} function {другому модулю. Заголовки процедур и функций в этом} {подразделе допускается указывать без списка формальных} {параметров. Если заголовки указаны все же с параметрами, то} {их список должен быть идентичен такому же списку для} {соответствующей процедуры (функции) в разделе interface}

{Раздел инициализации }

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

end .

Заголовок модуля и связь модулей друг с другом

Заголовок модуля состоит из зарезервированного слова unit и следующего за ним имени модуля. Для правильной работы среды Турбо Паскаля и возможности подключения средств, облегчающих разработку больших программ, имя модуля должно совпадать с именем дискового файла , в который помещается исходный текст модуля. Если, например, имеем заголовок модуля

Unit primer;

то исходный текст этого модуля должен размещаться на диске в файле primer.pas.

Имя модуля служит для его связи с другими модулями и основной программой. Эта связь устанавливается специальным предложением:

uses <список модулей>

Здесь USES – зарезервированное слово (использует);

<список модулей> - список модулей, с которыми устанавливается связь; элементы списка – имена модулей через запятую.

Если в программе модули используются, то предложение uses <список модулей> должно стоять сразу после заголовка программы , т.е. должно открывать раздел описаний основной программы. В модулях могут использоваться другие модули. В модулях предложение uses <список модулей> может стоять сразу после слова interface или сразу после слова implementation. Допускается и два предложения uses, т.е. оно может стоять и там, и там.

Интерфейсная часть

Интерфейсная часть открывается зарезервированным словом INTERFACE . В этой части содержатся объявления всех глобальных объектов модуля (типов, констант, переменных и подпрограмм), которые должны быть доступны основной программе и (или) другим модулям. При объявлении глобальных подпрограмм в интерфейсной части указывается только их заголовок, например:

Procedure AddC(x,y: complex, var z: complex);

Procedure MulC (x,y: complex, var z: complex);