Ada (798525), страница 15
Текст из файла (страница 15)
При этом, пакетпозволяет программисту скрыть все детали реализации сервисов за развитым функциональныминтерфейсом. В результате, структурное представление программного комплекса в виде наборавзаимодействующих между собой компонентов облегчает понимание работы комплекса в целоми, следовательно, позволяет облегчить его разработку и сопровождение.Необходимо также заметить, что на этапе начального проектирования системы можнопредоставлять компилятору только спецификации, обеспечивая детали реализации только самыхнеобходимых элементов.
Таким образом проверка корректности общей структуры проектаосуществляется на ранней стадии, когда не потрачено много усилий на разработку реализацииотдельных элементов, которые позже придется переделывать (что, к великому сожалению, вреальной жизни происходит достаточно часто).Примечание:В системе компилятора GNAT существует соглашение согласно которому файлыспецификаций имеют расширение ads (ADa Specification), а файлы тел имеютрасширение adb (ADa Body).7.1.2 Спецификация пакетаКак уже говорилось, спецификация пакета Ады определяет интерфейс доступа квычислительным ресурсам (сервисам) пакета.
Она может содержать описания типов,переменных, констант, спецификации подпрограмм, других пакетов, - все то, что должно бытьдоступно тому, кто будет использовать данный пакет. Простой пример спецификации пакетаможет иметь следующий вид:package Odd_Demo istype A_String is array (Positive range <>) of Character;Pi : constant Float := 3.14;X : Integer;type A_Record isrecordLeft : Boolean;Right : Boolean;end record;-- примечательно, что дальше, для двух подпрограмм представлены только-- их спецификации, тела этих подпрограмм будут находиться в теле пакетаprocedure Insert(Item : in Integer; Success : out Boolean);function Is_Present(Item : in Integer) return Boolean;end Odd_Demo;Мы можем получить доступ к этим сервисам в нашем коде путем указания данного пакета вспецификаторе совместности контекста with, а затем использовать полную точечную нотацию.Полная точечная нотация, в общем случае, имеет следующий вид:имя_пакета.имя_используемого_ресурсагде имя_используемого_ресурса - это имя типа, подпрограммы, переменной и т.д.Для демонстрации сказанного приведем схематический пример процедуры которая используетпоказанную выше спецификацию:with Odd_Demo;procedure Odder_Demo isMy_Name : Odd_Demo.A_String;Radius : Float;Success : Boolean;beginRadius := 3.0 * Odd_Demo.Pi;Odd_Demo.Insert(4, Success);if Odd_Demo.Is_Present(34) then ......end Odder_Demo;Не трудно заметить, что доступ к ресурсам пакета, текстуально подобен доступу к полям записи.В случаях, когда использование полной точечной нотации для доступа к ресурсам пакетаобременительно, можно использовать инструкцию спецификатора использования контекстаuse.
Это позволяет обращаться к ресурсам которые предоставляет данный пакет безиспользования полной точечной нотации, так, как будто они описаны непосредственно в этом жекоде.with Odd_Demo;use Odd_Demo;procedure Odder_Demo isMy_Name : A_String;Radius : Float;Success : Boolean;beginRadius := 3.0 * Pi;Insert(4, Success);if Is_Present(34) then ......end Odder_Demo;Если два пакета, указаны в инструкциях with и use в одном компилируемом модуле (например,в подпрограмме или другом пакете), то возможно возникновение коллизии имен используемыхресурсов, которые предоставляются двумя разными пакетами.
В этом случае можно избавитьсяот двусмысленности путем возвращения к использованию полной точечной нотации длясоответствующих ресурсов.------------------------------package No1 isA, B, C : Integer;end No1;------------------------------package No2 isC, D, E : Integer;end No2;------------------------------with No1;use No1;with No2;use No2;procedure Clash_Demo isbeginA := 1;B := 2;C := 3;-- двусмысленность, мы ссылаемся-- на No1.c или на No2.c?No1.C := 3;-- избавление от двусмысленности путем возвратаNo2.C := 3; -- к полной точечной нотацииend Clash_Demo;Может возникнуть другая проблема - когда локально определенный ресурс "затеняет" ресурспакета указанного в инструкции use.
В этом случае также можно избавиться отдвусмысленности путем использования полной точечной нотации.package No1 isA : Integer;end No1;with No1;procedure P isuse No1;A : Integer;beginA := 4;-- это - двусмысленноP.A := 4;-- удаление двусмысленности путем указания-- имени процедуры в точечной нотацииNo1.A := 5; -- точечная нотация для пакетаend P;7.1.3 Тело пакетаТело пакета содержит все детали реализации сервисов, указаных в спецификации пакета.Схематическим примером тела пакета, для показанной выше спецификации, может служить:package body Odd_Demo istype List is array (1..10) of Integer;Storage_List : List;Upto: Integer;procedure Insert(Item: in Integer;Success : out Boolean) isbegin...end Insert;function Is_Present(Item : in Integer) return Boolean isbegin...end Is_Present;begin-- действия по инициализации пакета-- это выполняется до запуска основной программы!for I in Storage_List'Range loopStorage_List(I) := 0;end loop;Upto := 0;end Odd_Demo;Все ресурсы, указанные в спецификации пакета, будут непосредственно доступны в теле пакетабез использования дополнительных инструкций спецификации контекста with и/или use.Необходимо заметить, что тело пакета, также как и спецификация пакета, может содержатьописания типов, переменных, подпрограмм и т.д.
При этом, ресурсы, описанные в теле пакета,не доступны для использования в другом самостоятельном модуле (пакете или подпрограмме).Любая попытка обращения к ним из другого модуля будет приводить к ошибке компиляции.Переменные, описанные в теле пакета, сохраняют свои значения между успешными вызовамипублично доступных подпрограмм пакета. Таким образом, мы можем создавать пакеты, которыесохраняют информацию для более позднего использования (другими словами: сохранятьинформацию о состоянии).Раздел "begin ... end", в конце тела пакета, содержит перечень инструкций инициализации дляэтого пакета. Инициализация пакета выполняется до запуска на выполнение главнойподпрограммы. Это справедливо для всех пакетов. Следует заметить, что стандарт не определяетпорядок выполнения инициализации различных пакетов.7.2 Средства сокрытия деталей реализации внутреннего представленияданныхКак уже указывалось, спецификация пакета определяет интерфейс, а его тело скрываетреализацию сервисов предоставляемых пакетом.
Однако, в примерах, показанных ранее, отпользователей пакетов были скрыты только детали реализации подпрограмм, а все типы данныхбыли открыто описаны в спецификации пакета. Следовательно, все детали реализациипредставления внутренних структур данных "видимы" пользователям. В результате,пользователи таких пакетов, полагаясь на открытость представления внутренних структурданных и используя эти сведения, попадают в зависимость от деталей реализации структурданных.
Это значит, что в случае какого-либо изменения во внутреннем представлении данных,как минимум, возникает необходимость в полной перекомпиляции всех программных модулейкоторые используют такую информацию. В худшем случае, зависимые модули придется нетолько перекомпилировать, но и переделать.На первый взгляд, сама проблема выглядит достаточно безобидно, а идея скрыть реализациювнутреннего представления данных кажется попыткой ущемления здорового любопытствапользователя. Справедливо заметить, что такие мысли, как правило, возникают при видепростых структур данных представленных в учебных примерах. К сожалению, в реальнойжизни все сложнее.
Представьте себе последствия изменения внутреннего представленияданных в реальном проекте когда зависимых модулей много и разрабатываются эти модулиразными программистами.Ада позволяет "закрыть" детали внутреннего представления данных и, таким образом,избавиться от проблемы массовых переделок. Такие возможности обеспечивает использованиеприватных типов (private types) и лимитированных приватных типов (limited private types).7.2.1 Приватные типы (private types)Рассмотрим пример пакета который управляет счетами в бухгалтерской книге.
При этом, намнеобходимо осуществлять полный контроль над всеми манипуляциями которые выполняются собъектами, и мы обеспечиваем пользователям пакета только следующие возможности:- изъятие средств (Withdraw)- размещение средств (Deposit)- создание счета (Create)Никакие другие пакеты не должны иметь представления о деталях реализации внутреннейструктуры объекта бухгалтерского счета (Account) и, следовательно, иметь к ним доступ.Для того чтобы выполнить поставленную задачу, мы описываем объект бухгалтерского счетаAccount как приватный тип:package Accounts istype Account is private;-- описание будет представлено позжеprocedure Withdraw(An_Account : in out Account;Amount: inMoney);procedure Deposit( An_Account : in out Account;Amount: inMoney);function Create( Initial_Balance : Money) return Account;function Balance( An_Account : in Account) return Integer;private-- эта часть спецификации пакета-- содержит полные описанияtype Account isrecordAccount_No : Positive;Balance: Integer;end record;end Accounts;В результате такого описания, тип Account будет приватным.
Следут заметить, что Адаразрешает использовать следующие предопределенные операции над объектами приватноготипа вне этого пакета:- присваивание- проверка на равенство (не равенство)- проверки принадлежности ("in", "not in")Кроме предопределенных операций, над объектами приватного типа, вне этого пакета,разрешается использовать операции, которые объявлены как подпрограммы в спецификациипакета (обычные процедуры и функции, а также функции реализующие действия знаковопераций).Все детали реализации внутреннего представления приватного типа доступны в теле пакета, илюбая подпрограмма в теле пакета имеет к ним доступ и может модифицировать приватный типкак обычный тип.
Таким образом, приватность типа сохраняется только вне пакета.В данном примере необходимо обратить внимание на то, что спецификация пакета разделена надве части. Все что находится до зарезервированного слова private - это общедоступная частьописаний, которая будет "видна" всем пользователям пакета. Все что находится послезарезервированного слова private - это приватная часть описаний, которая будет "видна"только внутри пакета (и в его дочерних модулях; см.