Прочность модулей
Прочность модуля – это мера его внутренних связей. Чтобы определить прочность модуля, необходимо проанализировать выполняемую им функцию (или функции), с тем, чтобы решить, к какому из семи классов он относится. Классы эти специально определены для того, чтобы ввести количественную характеристику «доброкачественности» конкретных типов модулей.
В первую очередь необходимо определить, что понимается под функцией модуля. Модуль имеет три основных атрибута: он выполняет одну или несколько функций, обладает некоторой логикой и используется в одном или нескольких контекстах. Функция – это внешнее описание модуля; описывается, что делает модуль, когда он вызван, но не как это делается. Логика описывает внутренний алгоритм модуля, другими словами, как он выполняет свою функцию. Контекст описывает конкретное применение модуля. Например, модуль с функцией «удалить пробелы из литерной строки» может использоваться в контексте «сжать сообщение для телеобработки». Чтобы увидеть разницу между функцией и логикой, рассмотрим модуль, функция которого – зашифровать введенную информацию по заданному ключу. Он может быть головным модулем 15-модульной программы либо единственным модулем программы. В обоих случаях функция этих двух модулей одинакова, но логика – совершенно разная. Мы видим, что функция модуля может рассматриваться как композиция его логики и функций всех подчиненных (вызываемых им) модулей. Это определение рекурсивно и применимо к любому модулю в иерархии.
Цель проектирования – так определить модули, чтобы каждый из них выполнял одну функцию (говорят, что такие модули обладают функциональной прочностью). Чтобы понять важность этой цели, ниже рассмотрим семь классов прочности модулей [11], начиная с самого слабого типа прочности.
Модуль, прочный по совпадению, – модуль, между элементами которого нет осмысленных связей. Трудно привести пример такого модуля, поскольку он не выполняет никаких разумных функций. Описание логики – единственный возможный способ описания модулей этого типа. Одна из причин, по которым такие модули могут возникнуть, – это «модуляризация» программы post factum, когда мы обнаруживаем одинаковые последовательности команд в нескольких модулях и решаем сгруппировать их в отдельный модуль. Если эти последовательности (хотя они и кажутся идентичными) имеют разный смысл в тех модулях, в которые они первоначально входили, то наш новый модуль является прочным по совпадению. Модуль этого типа тесно связан с вызывающими его модулями, поэтому почти любая его модификация в интересах одного из этих модулей приводит к тому, что для всех остальных он станет работать неправильно.
Модуль, прочный по логике, содержит несколько функций, и при каждом вызове выполняет одну из них. Выбираемая функция обычно запрашивается вызывающим модулем, например с помощью кода функции. Примером может быть модуль, функция которого – читать из файла или писать в файл. Главная проблема с модулями этого типа – это использование одного и того же сопряжения для выполнения многих функций. Это приводит к сложным сопряжениям и неожиданным ошибкам при изменении сопряжения ради одной из функций.
Модуль, прочный по классу, последовательно выполняет набор связанных с ним функций. Самые распространенные примеры – «начальный» и «заключительный» модули. Главная проблема с модулями этого типа состоит в том, что обычно они неявно связаны с другими модулями программы, что делает программу трудной для понимания и ведет к ошибкам, когда ее приходится изменять.
Процедурно прочный модуль последовательно выполняет набор тех связанных с ним функций, которые непосредственно относятся к процедуре решения задачи. Вот пример задачи для подсистемы «интеллектуальный дом»: написать программу регулирования температуры простого парового котла. В определенной степени подобная задача может определять действия программы. Например, в постановке задачи может быть сказано, что при получении сигнала х следует закрыть клапан у и прочитать и зарегистрировать значение температуры. Модуль с функцией «закрыть клапан у, прочитать значение температуры парового котла и занести его в журнал» обладает процедурной прочностью. В этом случае единственная проблема, связанная с надежностью, состоит в том, что фрагменты программы, относящиеся к различным функциям, могут быть переплетены. Отметим, что для модулей этого типа, так же как и для большинства других типов, имеются и другие, не связанные с надежностью проблемы, как показано в [11].
Коммуникационно прочный модуль – это процедурно прочный модуль с одним дополнительным ограничением: все его функции связаны по данным. Например, модуль «прочитать следующую запись и обновить главный файл» коммуникационно прочен, поскольку обе его функции связаны между собой тем, что обе они работают с одной и той же записью. Здесь обычно возможно переплетение функций, но риск внесения ошибки при модификации несколько меньше, поскольку функции связаны более тесно.
Функционально прочный модуль – это модуль, выполняющий одну определенную функцию, такую как «посчитать вероятность правильной передачи сообщения», «закрыть клапан у» или «подвести итог по расходу горячей воды за месяц». Функциональная прочность – это высшая (лучшая) форма прочности модуля.
Отметим, что функционально прочный модуль может быть описан набором более детальных функций. Например, модуль «подвести итог по расходу горячей воды за месяц» можно описать так: «подготовить начальное состояние итоговой таблицы, открыть файл расхода горячей воды, читать расход и обновлять итоговую таблицу». Глядя на это, читатель может подумать, что простой перефразировкой описания модуля понижена его прочность. Однако если эти «функции более низкого уровня» могут быть рационально описаны как одна хорошо определенная функция «более высокого уровня», то следует считать, что модуль обладает функциональной прочностью.
Оставшийся тип прочности – информационная прочность. Информационно прочный модуль выполняет несколько функций, причем все они работают с одной и той же структурой данных и каждая представляется собственным входом. Модуль с двумя входами, один из которых соответствует функции «включить элемент в базу данных», а другой – функции «искать в базе данных», обладает информационной прочностью. Модуль этого типа может также рассматриваться как физическое объединение нескольких функционально прочных модулей с целью «упрятывания информации» [12], например, для того, чтобы укрыть внутри одного модуля все сведения о конкретной структуре данных, ресурсах или устройстве. В упомянутом выше примере вся информация о структуре и расположении таблицы символов скрыта внутри одного модуля. Это имеет то преимущество, что всякий раз, когда удается скрыть некоторый аспект программы внутри одного модуля, независимость ее модулей увеличивается. Упоминавшуюся ранее цель проектирования нужно теперь подправить, чтобы наряду с функционально прочными модулями стремиться к информационно прочным.
Хотя выше мы сконцентрировали внимание только на связи между прочностью модуля и защищенностью от ошибок, прочность модуля влияет также на адаптируемость программы, трудность тестирования отдельных модулей и степень применимости модуля в других контекстах и других программах [11]. Шкала прочности упорядочена с учетом всех этих атрибутов.
Отметим, что модуль может соответствовать описанию нескольких типов прочности. Например, коммуникационно прочный модуль удовлетворяет также определению процедурной прочности и прочности по классу. Будем всегда относить модуль к высшему типу прочности, определению которого он удовлетворяет.