«Забытые» парадигмы программирования. Модульное программирование Виды программирования модульное

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

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

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

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

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

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

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

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

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

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

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

· потребность организационного расчленения крупных разработок;

· возможность параллельного исполнения модулей (в контексте параллельного программирования).

Определения модуля и его примеры. Приведем несколько дополнительных определений модуля.

· Модуль - это совокупность команд, к которым можно обратиться по имени.

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

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

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

· описание семантики функций, выполняемых модулем по каждому из его входов.

Разновидности модулей. Существуют три основные разновидности модулей:

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) рутинности (идемпотентность, независимость от предыдущих обращений) модуля.

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

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

· В большинстве случаев делаем модуль рутинным, т. е. независимым от предыдущих обращений.

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

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

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

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

Процедурный (или структурный - по названию подхода);

Объектный.

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

Результатом объектной декомпозиции является совокупность объектов, которые затем реализуют как переменные некоторых специально разрабаты­ваемых типов (классов), представляющих собой совокупность полей данных и методов, работающих с этими полями.

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

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

Данные модуль может получать и/или возвращать через общие области памяти или параметры.

Первоначально к модулям (еще понимаемым как подпрограммы) предъ­являлись следующие требования:

Отдельная компиляция;

Одна точка входа;

Одна точка выхода;

Соответствие принципу вертикального управления;

Возможность вызова других модулей;

Небольшой размер (до 50-60 операторов языка);

Независимость от истории вызовов;

Выполнение одной функции.

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



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

отдельно компилируемую библиотеку ресурсов, требование независимости модулей стало основным.

Практика показала, что чем выше степень независимости модулей, тем:

Легче разобраться в отдельном модуле и всей программе и, соответственно, тестировать, отлаживать и модифицировать ее;

Меньше вероятность появления новых ошибок при исправлении ста­рых или внесении изменений в программу, т. е. вероятность появления «волнового» эффекта;

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

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

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

Различают пять типов сцепления модулей:

По данным;

По образцу;

По управлению;

По общей области данных;

По содержимому.

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

Например, функция Мах предполагает сцепление по данным через па­раметры скалярного типа:

Function Max(a, b: integer) : integer;

If a>b then Max:=a else Max: =b;

Сцепление по образцу предполагает, что модули обмениваются данны­ми, объединенными в структуры. Этот тип также обеспечивает неплохие ха­рактеристики, но они хуже, чем у предыдущего типа, так как конкретные пе­редаваемые данные «спрятаны» в структуры, и потому уменьшается «про­зрачность» связи между модулями. Кроме того, при изменении структуры передаваемых данных Необходимо модифицировать все использующие ее модули.

Так, функция MaxEl, описанная ниже, предполагает сцепление по образ­цу (параметр а - открытый массив).

Function MaxEl(a:array of integer):integer;

Var i:word;

MaxEl:=a;

for i:=l to High(a) do

if a[i]>MaxEl then MaxEl: =a[i];

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

Например, функция MinMax предполагает сцепление по управлению, так как значение параметра flag влияет на логику программы: если функция MinMax получает значение параметра flag, равное true, то возвращает макси­мальное значение из двух, а если false, то минимальное:

Function MinMax(a, b: integer; flag: boolean): integer;

if(a>b) and (flag) then MinMax: =a

else MinMax: =b;

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

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

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

Например, функция МахА, использующая глобальный массив А, сцеп­лена с основной программой по общей области:

Function MaxA:integer; Var i:word;

MaxA: =a; for i:= Low(a)+l to High(a) do if a[i]>MaxA then MaxA: =a[i];

Следует иметь в виду, что «подпрограммы с памятью», действия кото­рых зависят от истории вызовов, используют сцепление по общей области, что делает их работу в общем случае непредсказуемой. Именно этот вариант используют статические переменные С и C++.

В случае сцепления по содержимому один модуль содержит обращения к внутренним компонентам другого (передает управление внутрь, читает и/или изменяет внутренние данные или сами коды), что полностью противо­речит блочно-иерархическому подходу. Отдельный модуль в этом случае уже не является блоком («черным ящиком»): его содержимое должно учитывать­ся в процессе разработки другого модуля. Современные универсальные язы­ки процедурного программирования, например Pascal, данного типа сцепле­ния в явном виде не поддерживают, но для языков низкого уровня, например Ассемблера, такой вид сцепления остается возможным.

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

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

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

Связность модулей. Связность - мера прочности соединения функци­ональных и информационных объектов внутри одного модуля. Если сцепле­ние характеризует качество отделения модулей, то связность характеризует степень взаимосвязи элементов, реализуемых одним модулем. Размещение сильно связанных элементов в одном; модуле уменьшает межмодульные свя­зи и, соответственно, взаимовлияние модулей. В то же время помещение сильно связанных элементов в разные модули не только усиливает межмо­дульные связи, но и усложняет понимание их взаимодействия. Объединение слабо связанных элементов также уменьшает технологичность модулей, так как такими элементами сложнее мысленно манипулировать.

Различают следующие виды связности (в порядке убывания уровня):

Функциональную;

Последовательную;

Информационную (коммуникативную);

Процедурную;

Временную;

Логическую;

Случайную.

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

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

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

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

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

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

Временная связность функций подразумевает, что эти функции выпол­няются параллельно или в течение некоторого периода времени (рис. 2.1, д). Временная связность данных означает, что они используются в некотором временном интервале. Например, временную связность имеют функции, вы­полняемые при инициализации некоторого процесса. Отличительной осо­бенностью временной связности является то, что действия, реализуемые та­кими функциями, обычно могут выполняться в любом порядке. Содержание модуля с временной связностью функций имеет тенденцию меняться: в него могут включаться новые действия и/или исключаться старые. Большая веро­ятность модификации функции еще больше уменьшает показатели техноло­гичности модулей данного вида по сравнению с предыдущим.

Логическая связь базируется на объединении данных или функций в од­ну логическую группу (рис. 2.1, е). В качестве примера можно привести функции обработки текстовой информации или данные одного и того же ти­па. Модуль с логической связностью функций часто реализует альтернатив­ные варианты одной операции, например, сложение целых чисел и сложение вещественных чисел. Из такого модуля всегда будет вызываться одна какая-либо его часть, при этом вызывающий и вызываемый модули будут связаны по управлению. Понять логику работы модулей, содержащих логически свя­занные компоненты, как правило, сложнее, чем модулей, использующих вре­менную связность, следовательно их показатели технологичности еще ниже.

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

Обратите внимание, что в трех предпоследних случаях связь между не­сколькими подпрограммами в модуле обусловлена внешними причинами. А в последнем - вообще отсутствует. Это соответствующим образом проециру­ется на технологические характеристики модулей. В табл. 2.2 представлены характеристики различных видов связности по экспертным оценкам .

Анализ табл. 2.2 показывает, что на практике целесообразно использо­вать функциональную, последовательную и информационную связности.

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

Библиотеки ресурсов. Различают библиотеки ресурсов двух типов: библиотеки Подпрограмм и библиотеки классов.

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

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

В качестве средства улучшения технологических характеристик библи­отек ресурсов в настоящее время широко используют разделение тела моду­ля на интерфейсную часть и область реализации (секции Interface и Imple­mentation - в Pascal, h и срр-файлы в C++ и в Java).

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

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

Введение

1. Теоретическая часть

1.1.1 Процедуры и функции

1.1.2 Модули

1.1.3 Открытые массивы и строки

1.1.4 Нетипизированные параметры

1.2 Объектно-ориентированное программирование

Заключение

Список литературы

Введение

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

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

1. Построить таблицы по приведенным данным.

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

3. Результаты представить в виде консолидированной таблицы.

4. Сформировать и заполнить форму сводной ведомости.

5. Результаты представить в графическом виде.

Объектом изучения является модульное и объектно-ориентированное программирование.

1. Теоретическая часть

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

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

1.1.1 Процедуры и функции

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

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

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

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

Из основной программы данные могут быть получены:

· Неявно – с использованием глобальных констант и переменных;

· Явно – через параметры.

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

В свою очередь локальные ресурсы, объявлены в разделе описания подпрограммы, из программного блока, в разделе описания которого она определена, не доступны. В том случае, если в подпрограмме объявляется ресурс, имя которого совпадает с именем глобального ресурса, соответствующий глобальный ресурс в подпрограмме становится не доступным, «перекрывается».

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

В BorlandPascal параметры в подпрограмму могут передаваться тремя способами:

· Как значения – в подпрограмму передаются копии значений параметров, и никакие изменения этих копий не возвращаются в вызывающую программу;

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

· Как изменяемые переменные (именованные константы) – в подпрограмму, так же как и в предыдущем случае, передаются адреса фактических параметров, но при попытке изменить значение параметра компилятор выдает сообщение об ошибке; такие параметры при описании помечаются служебным словом const.

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

< переменная> :=< имя функции> (< фактические параметры> ).

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

Вызов процедуры и функции по-разному изображается на схеме алгоритма: вызов функции – в блоке «процесс» или блоке вывода, а для вызова процедуры используется специальный блок «предопределенный процесс». Схемы алгоритмов же самих подпрограмм в обоих случаях оформляются отдельно, причем вместо слова «начало» указывают имя подпрограммы, а вместо слова «конец» - указывают слово «возврат» или «return».

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

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

Все ресурсы модуля делятся на две группы: внешние – предназначенные для использования другими программными единицами, и внутренние – рабочие ресурсы данного модуля.

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

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

Interface

<интерфейсная секция>

[ Begin

<секция инициализации>]

End .

Имя модуля должно совпадать с именем файла, в котором он содержится. Результат компиляции модуля помещается в файл с тем же именем и расширением.tpu.

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

· Compile – компилируется только основная программа, все модули должны быть предварительно откомпилированы в файлы <имя модуля> .tpu и размещены либо в текущем каталоге, либо в одном из каталогов, указанных как источники файлов.tpu в настройках среды;

· Make – модули, для которых не обнаружены файлы.tpu, компилируются из соответствующих файлов.pas, которые должны находиться в текущем каталоге или в каталогах, указанных в настройках среды в качестве источников исходных файлов модулей;

· Build – все ранее откомпилированные модули.tpu игнорируются и все модули компилируются из своих исходных файлов заново.

В процессе отладки модулей целесообразно использовать режим Build, а при отладке программы – режим Compile

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

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

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

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

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

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

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

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

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

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

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

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

Заметим, что нужно различать использование слова "модуль", когда имеется в виду синтаксическая конструкция языков программирования (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 непосредственных потомков.

 
Статьи по теме:
Что делать если пропал звук на ноутбуке?
Ситуация, когда пропадает звучание в компьютере, работающем под операционной системой Виндовс 8, возникает довольно часто. Причин тому может быть несколько. Давайте подробно рассмотрим все возможные причины и пути их устранения. Проверка оборудования В не
Установка Скайпа на компьютер (пошаговая инструкция) Установка приложения скайп
Если вам нужна четкая инструкция по установке самой популярной программы для онлайнового интернет-общения, эта информация для вас! Как произвести бесплатно установку на русском языке? Установить Скайп на компьютер бесплатно просто. Сделайте следующее. Пош
Как пользоваться селфи палкой с кнопкой
Монопод - изобретение не новое. Но вот использовать его для личных фотографий с фронтальной камеры телефона (селфи) придумали не так давно. И если вы хоть раз фотографировались на фронталку, то знаете, насколько легче и удобнее подобрать правильный ракурс
Самостоятельная прошивка телефонов, смартфонов и планшетов Xiaomi
Полная инструкция по прошивке (или FaQ как прошить) смартфона Xiaomi Redmi Note 3 Pro. Разблокировка телефона, прошивка официальной или кастомной прошивки, руссификация, Unlock загрузчика, установка TWRP recovery. Кодовое имя Redmi Note 3 Pro (Snapdragon