Ada (798525), страница 16
Текст из файла (страница 16)
"Дочерние модули").Может показаться противоречивым размещение приватных описаний в спецификации пакета.Действительно, мы пытаемся скрыть детали реализации приватного объекта, и размещаем их вспецификации пакета, которая доступна. Однако, это необходимо для программ, которыеразмещают экземпляры объектов приватного типа поскольку компилятор, благодаря такойинформации, знает сколько необходимо зарезервировать места для размещения экземпляраобъекта приватного типа.Хотя читатель спецификации пакета видит как устроено реальное внутреннее представлениереализации приватного типа, это не обеспечивает его возможностью явным и допустимымспособом использовать эту информацию. При этом, примечательным является то, чтоэкземпляры объектов приватного типа могут быть созданы вне пакета.
Например:with Accounts;use Accounts;procedure Demo_Accounts isHome_Account : Account;Mortgage: Account;This_Account : Account;beginMortgage := Accounts.Create(Initial_Balance => 500.00);Withdraw(Home_Account, 50);...This_Account := Mortgage;-- присваивание приватного типа - разрешено-- сравнение приватных типовif This_Account = Home_Account then...end Demo_Accounts;7.2.2 Лимитированные приватные типы (limited private types)Хотя приватные типы позволяют разработчику получить значительный контроль над действиямипользователя, ограничив способности пользователя в манипулировании объектами, бываютслучаи когда необходимо запретить пользователю приватного типа использовать даже такиепредопределенные операции как сравнение и присваивание.В качестве демонстрации сказанного, рассмотрим следующий пример:package Compare_Demo istype Our_Text is private;...privatetype Our_Text (Maximum_Length : Positive := 20) isrecordLength : Index := 0;Value : String(1..Maximum_Length);end record;...end Compare_Demo;Здесь, тип Our_Text описан как приватный и представляет из себя запись.
В данной записи,поле длины Length определяет число символов которое содержит поле Value (другимисловами - число символов которые имеют смысл). Любые символы, находящиеся в позиции отLength + 1 до Maximum_Length будут нами игнорироваться при использовании этойзаписи. Однако, если мы попросим компьютер сравнить две записи этого типа, то он, в отличиеот нас, не знает предназначения поля Length. В результате, он будет последовательносравнивать значения поля Length и значения всех остальных полей записи. Очевидно, чтоалгоритм предопределенной операции сравнения в данной ситуации не приемлем, и намнеобходимо написать собственную функцию сравнения.Для подобных случаев Ада предусматривает лимитированные приватные типы. Изменимрассмотренный выше пример следующим образом:package Compare_Demo istype Our_Text is limited private;...function "=" (Left, Right : in Our_Text) return Boolean;...privatetype Our_Text (Maximum_Length : Positive := 20) isrecordLength : Index := 0;Value : String(1..Maximum_Length);end record;...end Compare_Demo;Теперь, тип Our_Text описан как лимитированный приватный тип.
Также, в спецификациипакета, описана функция знака операции сравнения на равенство "=". Реализация алгоритмаэтой функции должна быть помещена в тело пакета. Примечательно, что при совмещении знакаоперации равенства "=" автоматически производится неявное совмещение знака операциинеравенства "/=". При этом следует учесть, что если функция реализующая действие знакаоперации равенства "=" возвращает значение тип которого отличается от предопределенногологического типа Boolean (полное имя - Standard.Boolean), то совмещение знакаоперации неравенства "/=" необходимо описать явно. Следует заметить, что Ада разрешаетпереопределять знак операции равенства для всех типов.Для лимитированного приватного типа можно также создать процедуру для выполненияприсваивания (или инициализации).
Например, для показанного выше типа Our_Text,спецификация такой процедуры может иметь следующий вид:...procedure Init (T : in out Our_Text;S : inString);. . .Напомним, что спецификация такой процедуры должна быть размещена в спецификации пакетаCompare_Demo, а ее тело (реализация) - в теле этого пакета.7.2.3 Отложенные константы (deferred constants)В некоторых спецификациях пакетов возникает необходимость описать константу приватноготипа.
Это можно выполнить таким же образом как и описание приватного типа (а такжебольшинство опережающих ссылок). В общедоступной части спецификации пакета мы создаемнеполное описание константы, после чего, компилятор ожидает получить полное описаниеконстанты в приватной части спецификации пакета. Например:package Coords istype Coord is private;Home: constant Coord; -- отложенная константа!privatetype Coord is recordX : Integer;Y : Integer;end record;Home : constant Coord := (0, 0);end Coords;В результате такого описания, пользователи пакета Coords "видят" константу Home, котораяимеет приватный тип Coord, и могут использовать эту константу в своем коде.
При этом,детали внутреннего представления этой константы им не доступны и они могут о них незаботиться.7.3 Дочерние модули (child units) (Ada95)Не редко, во время интенсивной разработки большой системы, возникают случаи когда один изпакетов разрастается необычайно быстро, а его спецификация подвергается постояннымизменениям. Таким образом, ввиду частого обновления спецификации пакета, резко возрастаютзатраты на перекомпиляцию зависимых модулей, а увеличение размеров самого пакетаусложняет его дальнейшую разработку и последующее сопровождение.Для преодоления подобных трудностей Ада предлагает концепцию дочерних модулей, котораяявляется основой в построении иерархических библиотек.
Такой подход позволяет разделитьпакет большого размера на самостоятельные пакеты и подпрограммы меньшего размера,объединенные в общую иерархию. Кроме того, эта концепция позволяет расширять ужесуществующие пакеты. Она предлагает удобный инструмент для обеспечения множествареализаций одного абстрактного типа и дает возможность разработки самодостаточныхподсистем при использовании приватных дочерних модулей.Дочерние модули непосредственно используются в стандартной библиотеке Ады.
В частности,дочерними модулями являются такие пакеты как Ada.Text_IO, Ada.Integer_Text_IO. Следуеттакже заметить, что концепция дочерних модулей была введена стандартом Ada95. В стандартеAda83 эта идея отсутствует.7.3.1 Расширение существующего пакетаРассмотрим случай когда возникает необходимость расширения пакета который уже содержитнекоторое множество описаний. Например, в пакете Stacks может понадобиться дополнительныйсервис просмотра Peek. Если в текущий момент времени пакет Stacks используется многимимодулями, то такая модификация, путем добавления нового сервиса, потребует значительныхзатрат на перекомпиляцию всех зависимых модулей, причем, включая и те модули которые небудут использовать новый сервис.Следовательно, для логического расширения уже существующего пакета предпочтительнееиспользовать дочерний пакет который будет существовать абсолютно отдельно. Например:package Stacks istype Stack is private;procedure Push(Onto : in out Stack; Item : Integer);procedure Pop(From : in out Stack; Item : out Integer);function Full(Item : Stack) return Boolean;function Empty(Item : Stack) return Boolean;private-- скрытая реализация стека...-- точка Aend Stacks;package Stacks.More_Stuff isfunction Peek(Item : Stack) return Integer;end Stacks.More_Stuff;По правилам Ады, спецификация родительского пакета обеспечивает для дочернего пакетатакую же самую область видимости, что и для своего тела.
Следовательно, дочерний пакетвидит всю приватную часть спецификации родительского пакета.В показанном выше примере, пакет Stacks.More_Stuff является дочерним пакетом для пакетаStacks. Значит, дочерний пакет Stacks.More_Stuff "видит" все описания пакета Stacks, вплоть доточки A.Необходимо заметить, что для сохранения приватности описаний родительского пакета, Ада непозволяет включать в спецификацию дочернего пакета приватную часть спецификацииродительского пакета.Примечание:Согласно правил именования файлов, принятым в системе компилятора GNAT,спецификация и тело пакета Stacks должны быть помещены в файлы:stacks.ads и stacks.adbсоответственно, а спецификация и тело дочернего пакета Stacks.More_Stuff - в файлы:stacks-more_stuff.ads и stacks-more_stuff.adbКлиенту, которому необходимо использовать функцию Peek, просто необходимо включитьдочерний пакет в инструкцию спецификатора совместности контекста with:with Stacks.More_Stuff;procedure Demo isX : Stacks.Stack;beginStacks.Push(X, 5);if Stacks.More_Stuff.Peek = 5 then...end Demo;Следует заметить, что включение дочернего пакета в инструкцию спецификатора совместностиконтекста with автоматически подразумевает включение в инструкцию with всех пакетовродителей.
Однако, инструкция спецификатора использования контекста use таким образом неработает. То есть, область видимости может быть получена пакетом только в базисе пакета.with Stacks.More_Stuff; use Stacks; use More_Stuff;procedure Demo isX : Stack;beginPush(X, 5);if Peek(x) = 5 then...end Demo;Необходимо также заметить, что подпрограммы (процедуры и функции) могут быть дочернимимодулями пакета (правила их использования достаточно очевидны). При этом, однако, самиподпрограммы не могут иметь дочерние модули.7.3.2 Иерархия модулей как подсистемаКаждый программный модуль (процедура, функция пакет) должен иметь уникальное имя.Бывают случаи, когда происходит быстрое заполнение пространства имен. Например, припроектировании большой системы достаточно сложно обеспечить уникальность имен и приэтом сохранить их смысловое значение, а в случаях когда разные части проекта разрабатываютсяразными программистами (или даже коллективами программистов) риск получения коллизииимен увеличивается еще больше.В подобных ситуациях, используя концепцию дочерних модулей Ады, можно выделить каждуюотдельно разрабатываемую подсистему в самостоятельную иерархию модулей.
При этом,каждая подсистема будет иметь свой собственный корневой пакет с уникальным именем.package Root is-- корневой пакет может быть пустымend Root;Поскольку имя корневого пакета уникально, то имена дочерних модулей иерархии также будутуникальны, что, в результате, минимизирует вероятность коллизии имен. Кроме того, такойприем является удобным средством разделения большого проекта на логическисамостоятельные составные части.Примером использования подобного подхода может служить стандартная библиотека Ады,которая предстявляется как набор дочерних модулей трех корневых пакетов: Ada, Interfaces иSystem.7.3.3 Приватные дочерние модули (private child units)Дочерние модули могут быть приватными. Такая концепция позволяет создавать дочерниемодули, которые будут видимы только внутри иерархии родительского пакета.