ОО-механизмы языка Ada 95: пример
Текст ниже приведенного пакета иллюстрирует некоторые технические приемы Ada 95. Его смысл должен быть достаточно ясен для читателя. Для получения нового типа с дополнительными полями (форма наследования Ada 95), нужно объявить уже существующий тип, такой как ACCOUNT, как дескрипторный (tagged). Это, конечно, противоречит принципу Открыт-Закрыт, поскольку необходимо знать заранее, какие типы могут иметь потомков, а какие - нет. Множественное наследование отсутствует, так что тип new можно получить только из одного типа. Обратите внимание на синтаксис получения нового типа без добавления атрибутов (null record, к удивлению, без end).
package Accounts is type MONEY is digits 12 delta 0.01; type ACCOUNT is tagged private; procedure deposit (a: in out ACCOUNT; amount: in MONEY); procedure withdraw (a: in out ACCOUNT; amount: in MONEY); function balance (a: in ACCOUNT) return MONEY; type CHECKING_ACCOUNT is new ACCOUNT with private; function balance (a: in CHECKING_ACCOUNT) return MONEY; type SAVINGS_ACCOUNT is new ACCOUNT with private; procedure compound (a: in out SAVINGS_ACCOUNT; period: in Positive); private type ACCOUNT is tagged record initial_balance: MONEY := 0.0; owner: String (1..30); end record; type CHECKING_ACCOUNT is new ACCOUNT with null record; type SAVINGS_ACCOUNT is new ACCOUNT with record rate: Float; end record; end Accounts;Дескрипторные типы по-прежнему объявляются как записи. Основное свойство большинства ОО-языков - операции над типом являются частью типа и фактически определяют тип - здесь не работает. Подпрограммы задаются вне объявления типа и принимают в качестве аргумента значение типа. (В ОО-языках, deposit и т. д. будут частью объявления ACCOUNT, а compound - частью SAVINGS_ACCOUNT, им не требуются их первые аргументы.) Здесь же все, что требуется, - так это объявление подпрограмм и типа как части одного и того же пакета; им даже не нужно находиться рядом друг с другом. В приведенном примере, только расположение показывает читателю, что определенные программы концептуально связаны с определенными дескрипторными типами записей.
Это отличается от обычного взгляда на создание ОО ПО. Хотя дескрипторный тип и связанные с ним подпрограммы с теоретической точки зрения являются частью одного абстрактного типа данных, они не образуют синтаксической единицы. Это противоречит принципу Лингвистических Модульных Единиц, предполагающему тесную связь между модульной концепцией и синтаксической структурой.
Появление нового объявления для balance в SAVINGS_ ACCOUNT сигнализирует о переопределении. Процедуры withdraw и deposit не переопределяются. Как будет понятно, это означает, что Ada 95 использует механизм перегрузки для получения ОО-эффекта от переопределения подпрограмм. Не существует синтаксической метки (как redefine), сигнализирующей о переопределении. Чтобы увидеть, что функция balance в SAVINGS_ACCOUNT отличается от базовой версии в ACCOUNT, следует просмотреть весь текст пакета. В данном случае каждая версия подпрограммы находится рядом с соответствующим типом, с отступами для выделения этой связи, но это условность стиля, а не правило языка.
Дескрипторный тип может объявляться как abstract, соответствуя понятию отложенного класса. Подпрограмму также можно сделать abstract, не создавая для нее тело.
Функция, возвращающая результат абстрактного типа, должна сама быть абстрактной. Это правило сначала может показаться странным и помехой для написания эффективной функции, возвращающей, скажем, вершину стека фигур в предположении, что тип FIGURE абстрактный. В языке Ada, однако, результат такой функции обычно будет принадлежать не типу FIGURE, а "типу доступа", описывающему ссылки на экземпляры FIGURE. Так что можно будет написать эффективную функцию.
К сущностям дескрипторного типа можно применить динамическое связывание, как в следующем примере:
procedure print_balance (a: in ACCOUNT'Class) is -- Печать текущего баланса. begin Put (balance (a)); New_Line; end print_balance;Динамическое связывание следует задать явным образом. Подпрограмма объявляется как "выходящая за рамки класса" (classwide operation) заданием классификатора 'Class для типа аргумента.Это напоминает объявление в C++ любой динамически связываемой функции как "виртуальной". Только здесь клиент выбирает статическое или динамическое связывание.
Ada 95 позволяет определить "дочерний пакет" A1.B существующего пакета A. Это дает новому пакету возможность получить свойства из A и добавить свои собственные расширения и модификации. (Это понятие, конечно, близко к наследованию, но отличается от него.) Вместо объявления трех типов счетов в одном пакете, возможно, лучше было бы разделить пакет на три, где Accounts.Checking представляет CHECKING_ACCOUNT и его подпрограммы, а Accounts.Saving делает то же для SAVINGS_ACCOUNT.