14.5. Объектно-ориентированное программирование на языке Ada 95
В языке Ada 83 наличие пакетной конструкции обеспечивает полную поддержку инкапсуляции, а наличие производных типов частично обеспечивает наследование. Полного наследования нет, потому что, когда вы производите новый тип, то можете добавлять только новые операции, но не новые компоненты данных. Кроме того, единственный полиморфизм — это статический полиморфизм вариантных записей. В языке Ada 95 поддерживается полное наследование за счет того, что программисту дается возможность расширить запись производного типа. Чтобы обозначить, что родительский тип записи пригоден для наследования, его нужно объявить как теговый (tagged) тип записи:
package Airplane_Package is
type Airplane_Data is tagged
record
ID:String(1..80);
Speed: Integer range 0..1000;
Altitude: Integer range 0..100;
end record;
end Airplane_Package;
Этот тег аналогичен тегу в языке Pascal и дискриминанту в вариантных записях языка Ada, где он используется для того, чтобы различать разные типы, производные друг из друга. В отличие от этих конструкций, тег теговой записи неявный, и программист не должен явно к нему обращаться. Заглядывая вперед, скажем, что этот неявный тег будет использоваться, чтобы диспетчери-зовать вызовы подпрограмм для динамического полиморфизма.
Чтобы создать абстрактный тип данных, тип должен быть объявлен как приватный и полное объявление типа дано в закрытой части:
package Airplane_Package is
type Airplane_Data is tagged private;
procedure Set_ID(A: in out Airplane_Data; S: in String);
function Get_ID(A: Airplane_Data) return String;
procedure Set_Speed(A: in out Airplane_Data; I: in Integer);
function Get_Speed(A: Airplane_Data) return Integer;
procedure Set_Altitude(A: in out Airplane_Data; I: in Integer);
function Get_Altitude(A: Airplane_Data) return Integer;
private
type Airplane_Data is tagged
record
ID:String(1..80);
Speed: Integer range 0..1000;
Altitude: Integer range 0.. 100;
end record;
end Airplane_Package;
Подпрограммы, определенные внутри спецификации пакета, содержащей объявление тегового типа (наряду со стандартными операциями на типе), называются примитивными операциями, или операциями-примитивами (primitive operations) и являются подпрограммами, которые наследуются. Наследование выполняется за счет расширения (extending) тегового типа:
with Airplane_Package; use Airplane_Package;
package SST_Package is
type SST_Data is new Airplane_Data with
record
Mach: Float;
end record;
procedure Set_Speed(A: in out SST_Data; I: iri Integer);
function Get_Speed(A: SST_Data) return Integer;
end SST_Package;
Значения этого производного типа являются копиями значений родительского типа Airplane_Data вместе с (with) дополнительным полем записи Mach. Операции, определенные для этого типа, являются копиями элементарных подпрограмм; эти операции могут быть замещены. Конечно, для производного типа могут быть объявлены другие самостоятельные подпрограммы.
В языке Ada нет специального синтаксиса для вызова подпрограмм-примитивов:
A: Airplane_Data;
Set_Speed(A, 100);
С точки зрения синтаксиса объект А — это обычный параметр; И по его типу компилятор может решить, какую именно подпрограмму вызвать. Параметр называется управляющим, Потому что он управляет тем, какую подпрограмму выбрать. Управляющий параметр не обязан быть первым параметром, и их может быть несколько (при условии, что все они того же типа). Сравните это с языком C++, который использует специальный синтаксис, чтобы вы-звать подпрограмму, объявленную в классе:
C++ |
a.set_speed(100);
Объект а является отличимым получателем (distinguished receiver) сообщения set_speed. Отличимый получатель является неявным параметром, в данном случае обозначающим, что скорость (speed) будет установлена (set) для объекта а.
Динамический полиморфизм
Перед обсуждением динамического полиморфизма в языке Ada 95 мы должны коснуться различий в терминологии языка Ada и других объектно-ориентированных языков.
В языке C++ термин класс обозначает тип данных, который используется для создания экземпляров объектов этого типа. Язык Ada 95 продолжает использовать термины типы и объекты даже для теговых типов и объектов, которые известны в других языках как классы и экземпляры. Слово класс ис-| пользуется для обозначения набора всех типов, которые порождаются от об-|щего предка, в языке C++ мы их назвали семейством классов. Нижеследующее обсуждение лучше всего провести в правильной терминологии языка Ada 95; будьте внимательны и не перепутайте новое применение слова класс с его использованием в языке C++.
С каждым теговым типом Т связан тип, который обозначается как T'Class
и называется типом класса (class-wide type)". T'Class покрывает (covered) все
типы, производные от Т. Тип класса — это неограниченный тип, и объявить
объект этого типа, не задав ограничений, нельзя, подобно объявлению
неограниченного массива:
type Vector is array(lnteger range <>) of Float;
V1: Vector; -- Запрещено, нет ограничений
type Airplane_Data is tagged record . . . end record;
A1: Airplane_Data'Class: -- Запрещено, нет ограничений
Объект типа класса может быть объявлен, если задать начальное значение:
V2: Vector := (1 ..20=>0.0); -- Правильно, ограничен
Х2: Airplane_Data; -- Правильно, конкретный тип
ХЗ: SST_Data; -- Правильно, конкретный тип
А2: Airplane_Data'Class := Х2; -- Правильно, ограничен
A3: Airplane_Data'Class := ХЗ; --Правильно, ограничен
Как и в случае массива, коль скоро CW-объект ограничен, его ограничения изменить нельзя. CW-тип можно использовать в декларации локальных переменных подпрограммы, которая получает параметр CW-типа. Здесь снова полная аналогия с массивами:
procedure P(S: String; С: in Airplane_Data'Class) is
Local_String: String := S;
Local_Airplane: Airplane_Data'Class := C;
Begin
…
end P;
Динамический полиморфизм имеет место, когда фактический параметр имеет тип класса, в то время как формальный параметр — конкретного типа, принадлежащего классу:
with Airplane_Package; use Airplane_Package;
with SST_Package; use SST_Package;
procedure Main is
procedure Proc(C: in out Airplane_Data'Class; I: in Integer) is
begin
Set_Speed(C, I); -- Какого типа С ??
end Proc;
A: Airplane_Data;
S: SST_Data;
begin -- Main
Proc(A, 500); -- Вызвать с Airplane_Data
Proc(S, 1000); -- Вызвать с SST_Data end Main:
Фактический параметр С в вызове Set_Speed имеет тип класса, но имеются две версии Set_Speed с формальным параметром либо родительского типа, либо производного типа. Во время выполнения тип С будет изменяться от вызова к вызову, поэтому динамическая диспетчеризация необходима, чтобы снять неоднозначность вызова.
Рисунок 14.6 поможет вам понять роль формальных и фактических параметров в диспетчеризации. Вызов Set_Speed вверху рисунка делается с фактическим параметром типа класса. Это означает, что только при вызове подпрограммы мы знаем, имеет ли фактический параметр тип Airplane_Data или SST_Data. Однако каждое обтъявление процедуры, показанное внизу рисунка, имеет формальный параметр конкретного типа. Как показано стрелками, вызов должен быть отправлен в соответствии с типом фактического параметра.
Обратите внимание, что диспетчеризация выполняется только в случае необходимости; если компилятор может разрешить вызов статически, он так и сделает. Следующие вызовы не нуждаются ни в какой диспетчеризации, потому что вызов делается с фактическим параметром конкретного типа, а не типа класса:
Set_Speed(A, 500);
Set_Speed(S, 1000);
Точно так же, если формальный параметр имеет тип класса, то никакая диспетчеризация не нужна. Вызовы Ргос — это вызовы отдельной однозначной про-
цедуры; формальный параметр имеет тип класса, который соответствует фактическому параметру любого типа, относящегося к классу. Что касается рис. 14.7, то, если бы объявление Set_Speed было задано как:
procedure Set_Speed(A: in out Airplane'Class: I: in Integer);
то любой фактический параметр класса «вписался» бы в формальный параметр класса. Никакая диспетчеризация не нужна, потому что каждый раз вызывается одна и та же подпрограмма.
При ссылочном доступе указуемый объект так же может иметь CW-тип. Указатель при этом может указывать на любой объект, тип которого покрывается CW-типом, и диспетчеризация осуществляется просто раскрытием указателя:
type Class_Ptr is access Airplane_Data'Class;
Ptr: Class_Ptr := new Airplane_Data;
if (...) then Ptr := new SST_Data; end if;
Set_Speed(Ptr.all); -- На какой именно тип указывает Ptr??
- Глава 1
- 1.2. Процедурные языки
- 1.3. Языки, ориентированные на данные
- 1.4. Объектно-ориентированные языки
- 1.5. Непроцедурные языки
- 1.6. Стандартизация
- 1.7. Архитектура компьютера
- 1.8. Вычислимость
- 1.9. Упражнения
- Глава 2
- 2.2. Семантика
- 2.3. Данные
- 2.4. Оператор присваивания
- 2.5. Контроль соответствия типов
- 2.7. Подпрограммы
- 2.8. Модули
- 2.9. Упражнения
- Глава 3
- 3.1. Редактор
- 3.2. Компилятор
- 3.3. Библиотекарь
- 3.4. Компоновщик
- 3.5. Загрузчик
- 3.6. Отладчик
- 3.7. Профилировщик
- 3.8. Средства тестирования
- 3.9. Средства конфигурирования
- 3.10. Интерпретаторы
- 3.11. Упражнения
- Глава 4
- 4.1. Целочисленные типы
- I: Integer; -- Целое со знаком в языке Ada
- 4.2. Типы перечисления
- 4.3. Символьный тип
- 4.4. Булев тип
- 4.5. Подтипы
- 4.6. Производные типы
- 4.7. Выражения
- 4.8. Операторы присваивания
- 4.9. Упражнения
- Глава 5
- 5.1. Записи
- 5.2. Массивы
- 5.3. Массивы и контроль соответствия типов
- Подтипы массивов в языке Ada
- 5.5. Строковый тип
- 5.6. Многомерные массивы
- 5.7. Реализация массивов
- 5.8. Спецификация представления
- 5.9. Упражнения
- Глава 6
- 6.1. Операторы switch и case
- 6.2. Условные операторы
- 6.3. Операторы цикла
- 6.4. Цикл for
- 6.5. «Часовые»
- 6.6. Инварианты
- 6.7. Операторы goto
- 6.8. Упражнения
- Глава 7
- 7.1. Подпрограммы: процедуры и функции
- 7.2. Параметры
- 7.3. Передача параметров подпрограмме
- 7.4. Блочная структура
- 7.5. Рекурсия
- 7.6. Стековая архитектура
- 7.7. Еще о стековой архитектуре
- 7.8. Реализация на процессоре Intel 8086
- 7.9. Упражнения
- Глава 8
- 8.1 . Указательные типы
- 8.2. Структуры данных
- 8.3. Распределение памяти
- 8.4. Алгоритмы распределения динамической памяти
- 8.5. Упражнения
- Глава 9
- 9.1. Представление вещественных чисел
- 9.2. Языковая поддержка вещественных чисел
- 9.3. Три смертных греха
- Вещественные типы в языке Ada
- 9.5. Упражнения
- Глава 10
- 10.1. Преобразование типов
- 10.2. Перегрузка
- 10.3. Родовые (настраиваемые) сегменты
- 10.4. Вариантные записи
- 10.5. Динамическая диспетчеризация
- 10.6. Упражнения
- Глава 11
- 11.1. Требования обработки исключительных ситуаций
- 11.2. Исключения в pl/I
- 11.3. Исключения в Ada
- 11.5. Обработка ошибок в языке Eiffei
- 11.6. Упражнения
- Глава 12
- 12.1. Что такое параллелизм?
- 12.2. Общая память
- 12.3. Проблема взаимных исключений
- 12.4. Мониторы и защищенные переменные
- 12.5. Передача сообщений
- 12.6. Язык параллельного программирования оссаm
- 12.7. Рандеву в языке Ada
- 12.9. Упражнения
- Глава 13
- 13.1. Раздельная компиляция
- 13.2. Почему необходимы модули?
- 13.3. Пакеты в языке Ada
- 13.4. Абстрактные типы данных в языке Ada
- 13.6. Упражнения
- Глава 14
- 14.1. Объектно-ориентированное проектирование
- В каждом объекте должно скрываться одно важное проектное решение.
- 14.3. Наследование
- 14.5. Объектно-ориентированное программирование на языке Ada 95
- Динамический полиморфизм в языке Ada 95 имеет место, когда фактический параметр относится к cw-типу, а формальный параметр относится к конкретному типу.
- 14.6. Упражнения
- Глава 15
- 1. Структурированные классы.
- 15.1. Структурированные классы
- 5.2. Доступ к приватным компонентам
- 15.3. Данные класса
- 15.4. Язык программирования Eiffel
- Если свойство унаследовано от класса предка более чем одним путем, оно используется совместно; в противном случае свойства реплицируются.
- 15.5. Проектные соображения
- 15.6. Методы динамического полиморфизма
- 15.7. Упражнения
- 5Непроцедурные
- Глава 16
- 16.1. Почему именно функциональное программирование?
- 16.2. Функции
- 16.3. Составные типы
- 16.4. Функции более высокого порядка
- 16.5. Ленивые и жадные вычисления
- 16.6. Исключения
- 16.7. Среда
- 16.8. Упражнения
- Глава 17
- 17.2. Унификация
- 17.4. Более сложные понятия логического программирования
- 17.5. Упражнения
- Глава 18
- 18.1. Модель Java
- 18.2. Язык Java
- 18.3. Семантика ссылки
- 18.4. Полиморфные структуры данных
- 18.5. Инкапсуляция
- 18.6. Параллелизм
- 18.7. Библиотеки Java
- 8.8. Упражнения