Модуль

Модуль — структурная единица программной системы, для которой характерны следующие свойства:

  1. структурные свойства
    1. система однозначно декомпонируется на подсистемы по границам модулей;
    2. конкретный модуль существует внутри системы в единственном экземпляре;
    3. запрещен циклический импорт модулей, модули образуют ориентированный ациклический граф (даг);
  2. свойства отношений
    1. модуль обеспечивает сокрытие своего внутреннего устройства, является черным ящиком;
    2. модуль обеспечивает инкапсуляцию описаний данных, состояния и кода;
    3. окружение взаимодействует с модулем только через опубликованный модулем интерфейс;
    4. внутри модуля действует правило no paranoia rule — между внутренними сущностями модуля отсутствует информационное сокрытие, они могут взаимодействовать напрямую наиболее эффективным способом;
    5. модуль имеет состояние и может динамически коммутироваться с другими модулями;
  3. свойства организации
    1. модуль является структурной единицей исходного кода (выраженной в виде синтаксической конструкции и обычно хранящейся в самостоятельном файле);
    2. модуль является единицей раздельной компиляции и безопасной компоновки с контролем совместимости по интерфейсам;
  4. с момента появления в 1989 г. Оберона как первого языка КОП важнейшим качеством современной модульности стала динамическая загрузка модулей, позволяющая «на лету» расширять программную систему новыми компонентами, в том числе выгрузить одну версию модуля и заменить ее на другую - без перекомпиляции и даже перезапуска системы. Это достигается исключением этапа статической компоновки и переноса его на этап загрузки модуля в память.

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

Значение модульности

Модульность является глобальным принципом современной инженерии. Основные достоинства модульности:

  1. уменьшается связность между частями системы;
  2. как следствие — повышается надежность, в частности — значительно снижается риск каскадного распространения последствий изменения части системы;
  3. за счёт информационного сокрытия появляется возможность прозрачной замены реализации, даже «на лету», в процессе работы системы. Различные реализации должны соблюсти общий интерфейс, но могут иметь произвольное внутреннее устройство.

Модульность в немодульных языках

Ряд распространённых промышленных языков (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 в С/С++. Никакого отношения к модулям и модульности эти механизмы не имеют. Все, что относится к разбиению на части исходного кода программы, целесообразно называть термином блок.

Ссылки по теме

© 2005-2024 OberonCore и коллектив авторов.