Модуль
Модуль — структурная единица программной системы, для которой характерны следующие свойства:
- структурные свойства
- система однозначно декомпонируется на подсистемы по границам модулей;
- конкретный модуль существует внутри системы в единственном экземпляре;
- запрещен циклический импорт модулей, модули образуют ориентированный ациклический граф (даг);
- свойства отношений
- модуль обеспечивает сокрытие своего внутреннего устройства, является черным ящиком;
- модуль обеспечивает инкапсуляцию описаний данных, состояния и кода;
- окружение взаимодействует с модулем только через опубликованный модулем интерфейс;
- внутри модуля действует правило no paranoia rule — между внутренними сущностями модуля отсутствует информационное сокрытие, они могут взаимодействовать напрямую наиболее эффективным способом;
- модуль имеет состояние и может динамически коммутироваться с другими модулями;
- свойства организации
- модуль является структурной единицей исходного кода (выраженной в виде синтаксической конструкции и обычно хранящейся в самостоятельном файле);
- модуль является единицей раздельной компиляции и безопасной компоновки с контролем совместимости по интерфейсам;
- с момента появления в 1989 г. Оберона как первого языка КОП важнейшим качеством современной модульности стала динамическая загрузка модулей, позволяющая «на лету» расширять программную систему новыми компонентами, в том числе выгрузить одну версию модуля и заменить ее на другую - без перекомпиляции и даже перезапуска системы. Это достигается исключением этапа статической компоновки и переноса его на этап загрузки модуля в память.
Непосредственная поддержка этих качеств модулей и контроль соблюдения правил модульности обеспечиваются модульными языками. Полноценная модульность позволяет строить кластерно-модульные архитектуры, обладающие большей гибкостью и надёжностью по сравнению с распространенными объектно-ориентированными архитектурами.
Значение модульности
Модульность является глобальным принципом современной инженерии. Основные достоинства модульности:
- уменьшается связность между частями системы;
- как следствие — повышается надежность, в частности — значительно снижается риск каскадного распространения последствий изменения части системы;
- за счёт информационного сокрытия появляется возможность прозрачной замены реализации, даже «на лету», в процессе работы системы. Различные реализации должны соблюсти общий интерфейс, но могут иметь произвольное внутреннее устройство.
Модульность в немодульных языках
Ряд распространённых промышленных языков (C, C++, Java, C#) синтаксически не поддерживают понятие модуля. Однако сам принцип модульной декомпозиции архитектуры и информационного сокрытия является ключевым для современной инженерии. Поэтому модульность в этих языках приходится эмулировать через другие наличные абстракции. Такой асбтракцией является «класс». Класс в этих языках — это одновременно и тип данных, и некоторое подобие модуля.
Класс как имитатор модуля обеспечивает свойства 2.1-2.3. Класс в Java также обеспечивает качества группы 3 и группы 4 - возможность динамической загрузки.
Однако понятие класса в немодульных ОО-языках оказывается семантически перегруженным. Класс может нести смысловую нагрузку типа данных, модуля, контейнера для объявлений, пространства видимости имен… Из-за возможности динамического создания экземпляров модуля не соблюдены структурные свойства группы 1, архитектура системы теряет жесткость и четкость, становится рыхлой.
Концептуальная ошибка в том, что в одном понятии «класс» смешаны две категории сущностей — элементы модели проблемной области (тип данных) и элементы архитектуры (модуль).
Игнорируемое правило 2.4 — no paranoia rule — ведет к необходимости накладных расходов на взаимодействия внутри модулей (там, где информационное сокрытие просто не нужно), либо к чрезмерно открытым интерфейсам. Как следствие это обусловливает проблемы для языков группы C++-Java-C# в системном программировании и традиционно отдаваемое предпочтение в этой области обычному C.
В распространённые языки часто вводятся дополнительные конструкции, которые являются попыткой «добавить модульность». В частности, это пространство имён в С++. Очевидно, что эта конструкция не обладает вообще ни одним из описанных выше свойств модулей. Вторая подобная конструкция - это пакеты . Пакеты поддерживают свойства 1.2, 1.3; 2.1-2.3; 3.1 и иногда 3.2. В частности, пакеты не поддерживают свойство 2.5 - и как следствие не могут являться основой для построения кластерно-модульных архитектур. В этом вопросе имеет место путаница терминологии - пакеты часто ошибочно называют модулями, как, например, в языке Haskell. В то же время полноценные модули в языке Ada исконно называются пакетами.
Вывод: модульные языки семантически богаче распространённых языков с чистым ООП, за счёт поддержки как абстракций моделирования на основе объектных типов данных, так и за счёт поддержки абстракций архитектуры на основе модулей. Особенно ценным это качество является для ИТ-образования, позволяя чётко разделять понятия и легко преподать основные схемы конструирования ПО. Эмулировать изученные подходы в немодульном языке можно достаточно легко, но хорошо усвоить их на основе последнего просто невозможно.
Микромодули
В 80-х годах термином «модуль» называли структурный элемент программы, обладающий интерфейсом и изолирующий свою внутреннюю реализацию. В частности, одной из разновидностью модуля считалась процедура. С другой стороны, точно таким же качеством обладают абстрактные и объектные типы данных. И то, и другое в ряде случаев может нести смысловую нагрузку модуля. Целесообразно ввести для объектных типов и процедур, функционирующих в режиме модуля, особый термин — микромодуль
. Отличительной особенностью микромодуля является возможность одновременного существования в системе многих его экземпляров и динамическое создание новых (несколько объектов одного типа, несколько активаций процедур).
Что не является модулем
Часто модулями ошибочно называют отдельные файлы исходного кода программы, а модульностью — механизмы препроцессора языка, подобные #include в С/С++. Никакого отношения к модулям и модульности эти механизмы не имеют. Все, что относится к разбиению на части исходного кода программы, целесообразно называть термином блок
.
Ссылки по теме
Автор: Ермаков. И.Е. Правки: Кривохатько С.А., PGR, Рюмшин Б.В.