ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 06.11.2023
Просмотров: 927
Скачиваний: 6
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
251 продуманный выбор компонентов. Разработчики выполняют последова- тельно то один, то другой из этих процессов до тех пор, пока не сведут исходную задачу к набору простых подзадач, которые несложно ре- шить.
Процесс абстракции можно рассматривать как некоторое обобщение.
Он позволяет забыть об особенностях информации и рассматривать различные объекты как если бы они были эквивалентны. Делается это с целью упрощения анализа, отделяя существенные атрибуты от несуще- ственных. Однако следует учитывать, что критерий такого отделения зависит от контекста.
Примером абстракции при программировании может служить кон- цепция файла. Файлы абстрагированы от конкретного носителя инфор- мации, реализуя долговременное и постоянно доступное хранилище поименованных единиц данных. (Файл – поименованная совокупность данных, хранящаяся на некотором носителе информации). Другой при- мер абстракции – языки высокого уровня. За счет использования ком- пилятора происходит абстрагирование от набора машинных команд, которые различны в разных компьютерах, выполняющих одну и ту же задачу, написанную на языке высокого уровня.
При использовании современных языков программирования про- граммист может создавать свои собственные абстракции по мере необ- ходимости. Наиболее распространенный механизм такого рода – ис- пользование процедур. Разделяя в программе тело процедуры и обра- щения к ней, язык высокого уровня реализует два важных метода абст- ракции: абстракция через параметризацию и абстракция через специа- лизацию [19].
Абстракция через параметризацию позволяет, используя парамет- ры, представить практически неограниченный набор различных вычис- лений одной программы, которая есть абстракция всех этих наборов.
Таким образом, абстракция через параметризацию – важное средство повышения универсальности программ.
Абстракция через спецификацию позволяет абстрагироваться от процессов вычислений (от алгоритма), описанных в теле процедуры, до уровня знания лишь того, что данная процедура должна в итоге реали- зовать. Это достигается созданием для процедуры спецификации, опи- сывающей эффект ее работы, после чего смысл обращения к данной процедуре становится ясным через анализ этой спецификации, а не са- мого тела процедуры.
Одним из способов задания спецификаций являются комментарии в виде пары утверждений. Первое утверждение (начальное условие) зада- ет на входе процедуры истинность или ложность некоторого условия
(или нескольких условий). Второе утверждение (конечное условие) за- дает некоторое условие, которое предполагается истинным по заверше- нию данной процедуры. Например, это может выглядеть так:
Имя_процедуры (список параметров)
// все параметры > 0
// возвращает наибольшее значение
252
{
тело процедуры
}
При анализе спецификации для уяснения смысла обращения к про- цедуре следует придерживаться двух четких правил:
1. После выполнения процедуры можно считать, что конечное условие выполнено.
2. Можно ограничиваться только теми свойствами, которые подразу- мевают конечное условие.
Абстракция через спецификацию по сравнению с абстракцией через параметризацию имеет следующие преимущества:
1. Используя процедуру, программисты не обязаны знакомиться с ее телом, т.е. не обязаны знать алгоритм, который она реализует. Это большое преимущество при работе со сложными процедурами.
2. Второе правило уточняет, что происходит абстрагирование от тела процедуры за счет отказа от несущественной информации.
Поскольку декомпозиция программных систем на основе абстракции связана с модуляризацией программной системы имеет смысл более подробно остановиться на понятии и характеристиках модулей.
1 ... 18 19 20 21 22 23 24 25 ... 37
6.2. Модульность
6.2.1. Модули, модульно-интерфейсный подход, модульное программирование
Для уменьшения сложности программной системы (ПС) она разби- вается на множество небольших, в высокой степени независимых моду- лей. Модуль – это замкнутая программа, которую можно вызвать из любого другого модуля системы. Это фрагмент программного текста, являющийся строительным блоком для физической структуры системы.
Как правило, модуль состоит из интерфейсной части и части- реализации. Модули можно разрабатывать на различных языках про- граммирования и отдельно компилировать. Высокой степени независи- мости модулей программной системы можно достичь с помощью двух методов оптимизации: усилением внутренних связей в каждом модуле и ослаблением взаимосвязи между ними.
Модульное программирование возникло в начале 60-х годов 20 века
[12, 40]. При создании программных систем оно дает следующие пре- имущества: упрощается разработка и реализация программных систем; появляется возможность одновременной (параллельной) работы ис- полнителей, что позволяет сократить сроки создания ПС; упрощается настройка и модификация ПС; возникает много естественных контрольных точек для наблюдения за продвижением проекта; можно создавать библиотеки наиболее употребительных программ; облегчается чтение и понимание программы;
253 обеспечивается более полное тестирование; становится проще процедура загрузки в оперативную память боль- шой ПС (эффективность распределения программы по страницам при работе в виртуальной памяти зависит от способа ее разбиения на модули).
Наряду с этими преимуществами имеются и некоторые недостатки, которые могут привести к возрастанию стоимости программной систе- мы: может увеличиться время исполнения программы; может возрасти размер требуемой памяти; может увеличиться время компиляции и загрузки; проблемы организации межмодульного взаимодействия могут ока- заться довольно сложными.
Перечислим основные свойства и требования к модулям [36, 40].
1. Модуль возникает в результате сепаратной компиляции или явля- ется частью результата совместной компиляции. Он может активизиро- ваться операционной системой или быть подпрограммой, вызываемой другим модулем.
2. На содержимое модуля можно ссылаться с помощью его имени.
3. Модуль должен возвращать управление тому, кто его вызвал.
4. Модуль может обращаться к другим модулям.
5. Модуль должен иметь один вход и один выход. Иногда программа с несколькими входами может оказаться короче и занимать меньше места в памяти. Однако опыт модульного программирования [40] пока- зал, что разработчики предпочитают иметь несколько похожих модулей, но не использовать несколько входов и выходов в одном модуле. Это объясняется тем, что единственность входа и выхода гарантирует замк- нутость модуля и упрощает сопровождение программной системы.
6. Модуль сравнительно невелик. Размеры модуля влияют на степень независимости элементов программы, легкость ее чтения и тестирова- ния. Обнаружено, что небольшие модули позволяют строить такие про- граммы, которые легче изменять. Такие модули чаще используются, они облегчают оценку и управление разработкой, их можно рекомендовать достаточно опытным и неопытным программистам. Можно было бы удовлетворить критериям высокой прочности и минимального сцепле- ния, спроектировав программу как несколько больших модулей, но вряд ли таким образом была бы достигнута высокая степень независимости.
Как правило, модуль должен содержать от 10 до 100 операторов языка высокого уровня (в некоторых публикациях – до 200).
С другой стороны, небольшие модули дольше проектируются, мед- леннее работают. Состоят все вместе из большего числа предложений исходного текста, требуют большей документации, их написание может быть менее приятным для программиста.
7. Модуль не должен сохранять историю своих вызовов для управ- ления своим функционированием. Такой модуль называют предсказуе-
мым. Модуль, хранящий следы своих состояний при последовательных вызовах не является предсказуемым. Все модули ПС должны быть
254 предсказуемыми, т.е. не должны сохранять никакой информации о пре- дыдущем вызове. Хитрые, неуловимые, зависящие от времени ошибки возникают в тех программах, которые пытаются многократно вызвать непредсказуемый модуль.
8. Структура принятия решения в модуле должна быть организова- на таким образом, чтобы те модули, на которые прямо влияет принятое решение, были подчиненными (вызываемыми) по отношению к прини- мающему решение модулю. Таким образом, обычно удается исключить передачу специальных параметров-индикаторов, представляющих ре- шения, которые должны быть приняты, а также принимать влияющие на управление программой решения на высоком уровне в иерархии про- граммы.
9. Минимизация доступа к данным. Объем данных, на которые мо- дуль может ссылаться, должен быть сведен к минимуму. Исключение сцепления по общей области, внешним данным и по формату – хороший шаг в этом направлении. Проектировщик должен попытаться изолиро- вать сведения о любой конкретной структуре данных или записи в базе данных в отдельном модуле (или небольшом подмножестве модулей), возможно за счет использования информационно прочных модулей.
10. Внутренние процедуры. Внутренняя процедура или подпрограм- ма – это замкнутая подпрограмма, физически расположенная в вызы- вающем ее модуле. Таких процедур следует избегать по нескольким причинам. Внутренние процедуры трудно изолировать для тестирова- ния, и они не могут быть вызваны из модулей, отличных от тех, которые их содержат. Это не соответствует идее повторного использования. Ко- нечно, имеется альтернативный вариант: включить копии внутренней процедуры во все модули, которым она нужна. Однако это часто приво- дит к ошибкам (копии часто становятся “несовсем точными”) и услож- няет сопровождение программы, поскольку, когда процедура изменяет- ся, все модули нужно перекомпилировать.
6.2.2. Обоснование модульности
Модульность – свойство системы, которая может подвергаться де- композиции на ряд внутренне связанных и слабо зависящих друг от друга модулей. По определению Г. Майерса, модульность – свойство программного обеспечения, обеспечивающее интеллектуальную воз- можность создания сколь угодно сложной программы [20]. Эту точку зрения можно проиллюстрировать следующим образом [30].
Пусть С(х) – функция сложности решения проблемы х, Т(х) – функ- ция затрат времени на решение проблемы х. Для двух проблем р1 и р2 из соотношения С(р1) > С(р2) следует, что
T(pl) > T(p2). (1)
Этот вывод интуитивно ясен: решение сложной проблемы требует большего времени. Из многолетней практики решения проблем челове- ком следует, что сложность решения большой проблемы выше, чем суммарная сложность решение ее частей:
255
С (pl + р2) > С (р1) + С (р2). (2)
Отсюда с учетом соотношения (1) получаем:
T (pl+p2) > T (pl) + T (p2). (3)
Соотношение (3) является обоснованием модульности. Оно приво- дит к известному заключению «разделяй и властвуй» – сложную про- блему легче решить, разделив ее на управляемые части. Результат, вы- раженный неравенством (3), имеет важное значение для модульности программных систем. Фактически, это аргумент в пользу модульности.
Однако здесь отражена лишь часть реальности, поскольку в данном случае не учитываются затраты на межмодульный интерфейс. Как пока- зано на рис. 6.1, с увеличением количества модулей (и уменьшением их размера) эти затраты также растут.
Рис. 6.1. Оптимальный размер модуля
Таким образом, существует оптимальное количество модулей Opt, которое приводит к минимальной стоимости разработки. К сожалению необходимого опыта для гарантированного предсказания значения Opt до сих пор не имеется. Однако опыт модульного конструирования про- граммных систем показывает, что оптимальный модуль должен удовле- творять двум критериям: снаружи он проще, чем внутри; его проще использовать, чем построить.
Принцип информационной закрытости, определенный Д. Парнасом еще в 1972 году, утверждает: содержание модулей должно быть скрыто друг от друга [4]. С развитием объектно-ориентированного программи- рования этот принцип стал одним из основополагающих. Модуль дол- жен определяться и проектироваться так, чтобы его содержимое (проце- дуры и данные) было недоступно тем модулям, которые не нуждаются в такой информации (клиентам).
Информационная закрытость означает следующее:
1) все модули независимы, обмениваются только информацией, не- обходимой для работы;
2) доступ к операциям и структурам данных модуля ограничен.
Достоинствами информационной закрытости являются:
256 возможность разработки модулей различными, независимыми кол- лективами; легкая модификация системы (вероятность распространения ошибок очень мала, так как большинство данных и процедур скрыто от дру- гих частей системы).
Идеальный модуль играет роль “черного ящика”, содержимое кото- рого невидимо клиентам. Он прост в использовании – количество “ру- чек и органов управления” им невелико (аналогия с эксплуатацией те- левизора). Его легко развивать и корректировать в процессе сопровож- дения программной системы [30]. Для обеспечения таких возможностей система внутренних и внешних связей модуля должна отвечать опреде- ленным требованиям. Рассмотрим характеристики внутренних и внеш- них связей модуля.
6.2.3. Внутренняя характеристика модуля – связность (прочность)
Связность (прочность) модуля (Cohesion) – это мера зависимости его частей [3, 5]. Связность – внутренняя характеристика модуля. Чем выше связность модуля, тем лучше результат проектирования, т.е. тем
«черней» его ящик (капсула, защитная оболочка модуля), тем меньше
«ручек управления» на нем находится и тем проще эти «ручки».
Для измерения связности используют понятие силы связности (СС).
Существует 7 типов связности (в различных источниках [14, 20, 30] данные несколько различаются):
1. Связность по совпадению (СС=0). В модуле отсутствуют явно вы- раженные внутренние связи.
2. Логическая связность (СС=1). Части модуля объединены по прин- ципу функционального подобия. Например, модуль состоит из разных подпрограмм обработки ошибок. При использовании такого модуля клиент выбирает только одну из подпрограмм.
Недостатками такой связности являются сложное сопряжение и большая вероятность внесения ошибок при изменении сопряжения ради одной из функций.
3. Временная связность (СС=3). Части модуля не связаны, но необ- ходимы в один и тот же период работы системы. Недостаток: сильная взаимная связь с другими модулями, отсюда – сильная чувствитель- ность внесению изменений.
4. Процедурная связность (СС=5). Части модуля связаны порядком выполняемых ими действий, реализующих некоторый сценарий поведе- ния.
5. Коммуникативная связность (СС=7). Части модуля связаны по данным (работают с одной и той же структурой данных).
6. Информационная (последовательная) связность (СС=9). Выходные данные одной части используются как входные данные в другой части модуля.
7. Функциональная связность (СС=10). Части модуля вместе реали- зуют одну функцию.