Руководство по Ada (Ada 2022)
3.9.4 Типы интерфейсов
Тип интерфейса — это абстрактный помеченный тип, предоставляющий ограниченную форму множественного наследования. Помеченный тип, тип задачи или защищённый тип могут иметь один или несколько типов интерфейса в качестве предков.
Синтаксис
определение_типа_интерфейса ::=
[ограниченный | задачи | защищённый | синхронизированный] интерфейс [и список_интерфейсов]
[ограниченный | задачи | защищённый | синхронизированный] интерфейс [и список_интерфейсов]
список_интерфейсов ::= маркер_подтипа_интерфейсамаркер_подтипа {и маркер_подтипа_интерфейсамаркер_подтипа}
Статическая семантика
Тип интерфейса (также называемый интерфейсом) — это специфический абстрактный помеченный тип, определяемый определением_типа_интерфейса.
Интерфейс с ключевыми словами ограниченный, задачи, защищённый или синхронизированный в его определении называется соответственно ограниченным интерфейсом, интерфейсом задачи, интерфейсом защищённого типа или синхронизированным интерфейсом. Кроме того, все интерфейсы задач и защищённых типов являются синхронизированными интерфейсами, а все синхронизированные интерфейсы являются ограниченными интерфейсами.
Тип задачи или защищённый тип, производный от интерфейса, является помеченным типом. Такой помеченный тип называется синхронизированным помеченным типом, как и синхронизированные интерфейсы и частные расширения, объявление которых включает ключевое слово синхронизированный.
Интерфейс задачи — это абстрактный тип задачи. Интерфейс защищённого типа — это абстрактный защищённый тип.
Тип интерфейса не имеет компонентов.
маркер_подтипа_интерфейсамаркер_подтипа в списке_интерфейсов называет подтип-предок; его тип — тип-предок. Тип интерфейса наследует определенные пользователем примитивные подпрограммы от каждого типа-предка так же, как производный тип наследует определенные пользователем примитивные подпрограммы от своих типов-предков (см. 3.4).
Правила легальности
Все определенные пользователем примитивные подпрограммы типа интерфейса должны быть абстрактными подпрограммами или нулевыми процедурами.
Тип подтипа, указанного в списке_интерфейсов, должен быть типом интерфейса.
Тип, производный от неограниченного интерфейса, должен быть неограниченным.
Интерфейс, производный от интерфейса задачи, должен включать ключевое слово задачи в своём определении; любой другой тип, производный от интерфейса задачи, должен быть частным расширением или типом задачи, объявленным с помощью объявления задачи (см. 9.1).
Интерфейс, производный от интерфейса защищённого типа, должен включать ключевое слово защищённый в своём определении; любой другой тип, производный от интерфейса защищённого типа, должен быть частным расширением или типом защищённого типа, объявленным с помощью объявления защищённого типа (см. 9.4).
Интерфейс, производный от синхронизированного интерфейса, должен включать одно из ключевых слов задачи, защищённый или синхронизированный в своём определении; любой другой тип, производный от синхронизированного интерфейса, должен быть частным расширением, типом задачи, объявленным с помощью объявления задачи, или типом защищённого типа, объявленным с помощью объявления защищённого типа.
Ни один тип не должен быть производным от интерфейса задачи и интерфейса защищённого типа одновременно.
Помимо мест, где обычно применяются Правила легальности (см. 12.3), эти правила также применяются в частной части экземпляра обобщённого модуля.
Динамическая семантика
Выполнение определения_типа_интерфейса создаёт тип интерфейса и его первый подтип.
ПРИМЕЧАНИЕ Неограниченные типы интерфейсов имеют предопределённые неоперационные операторы равенства. Эти операторы могут быть переопределены операторами равенства, определёнными пользователем. Такие операторы затем потребуют явного переопределения для любого неабстрактного потомка интерфейса.
Примеры
Пример ограниченного интерфейса и синхронизированного интерфейса, расширяющего его:
тип Очередь есть ограниченный интерфейс;
процедура Добавить(О : вход выход Очередь; Человек : вход Имя_Человека) есть абстрактный;
процедура Удалить_Первый(О : вход выход Очередь;
Человек : выход Имя_Человека) есть абстрактный;
функция Текущий_Счёт(О : вход Очередь) возвращает Натуральное_число есть абстрактный;
функция Максимальный_Счёт(О : вход Очередь) возвращает Натуральное_число есть абстрактный;
-- См. 3.10.1 для Имя_Человека.
процедура Добавить(О : вход выход Очередь; Человек : вход Имя_Человека) есть абстрактный;
процедура Удалить_Первый(О : вход выход Очередь;
Человек : выход Имя_Человека) есть абстрактный;
функция Текущий_Счёт(О : вход Очередь) возвращает Натуральное_число есть абстрактный;
функция Максимальный_Счёт(О : вход Очередь) возвращает Натуральное_число есть абстрактный;
-- См. 3.10.1 для Имя_Человека.
Ошибка_Очереди : исключение;
-- Добавить вызывает Ошибка_Очереди, если Текущий_Счёт(О) = Максимальный_Счёт(О)
-- Удалить_Первый вызывает Ошибка_Очереди, если Текущий_Счёт(О) = 0
-- Добавить вызывает Ошибка_Очереди, если Текущий_Счёт(О) = Максимальный_Счёт(О)
-- Удалить_Первый вызывает Ошибка_Очереди, если Текущий_Счёт(О) = 0
тип Синхронизированная_Очередь есть
синхронизированный интерфейс и Очередь; -- см. 9.11
процедура Добавить_Ожидание(О : вход выход Синхронизированная_Очередь;
Человек : вход Имя_Человека) есть абстрактный;
процедура Удалить_Первый_Ожидание(О : вход выход Синхронизированная_Очередь;
Человек : выход Имя_Человека) есть абстрактный;
синхронизированный интерфейс и Очередь; -- см. 9.11
процедура Добавить_Ожидание(О : вход выход Синхронизированная_Очередь;
Человек : вход Имя_Человека) есть абстрактный;
процедура Удалить_Первый_Ожидание(О : вход выход Синхронизированная_Очередь;
Человек : выход Имя_Человека) есть абстрактный;
...
процедура Перенос(От : вход выход Класс_Очереди;
К : вход выход Класс_Очереди;
Число : вход Натуральное_число := 1) есть
Человек : Имя_Человека;
начало
для I в 1..Число цикл
Удалить_Первый(От, Человек);
Добавить(К, Человек);
конец цикла;
конец Перенос;
К : вход выход Класс_Очереди;
Число : вход Натуральное_число := 1) есть
Человек : Имя_Человека;
начало
для I в 1..Число цикл
Удалить_Первый(От, Человек);
Добавить(К, Человек);
конец цикла;
конец Перенос;
Это определяет интерфейс очереди, определяющий очередь людей. (Аналогичный дизайн возможен для определения любого типа очереди, просто замените Имя_Человека соответствующим типом.) Интерфейс очереди имеет четыре диспетчерские операции: Добавить, Удалить_Первый, Текущий_Счёт и Максимальный_Счёт. Также показан тело операция для всего класса Перенос. Каждое неабстрактное расширение очереди предоставит реализации по крайней мере своих четырёх диспетчерских операций, так как они абстрактны. Любой объект типа, производного от очереди, может быть передан в Перенос в качестве операнда От или К. Два операнда могут быть разных типов в данном вызове.
Интерфейс Синхронизированная_Очередь наследует четыре диспетчерские операции от очереди и добавляет две дополнительные диспетчерские операции, которые ожидают, если необходимо, а не вызывают исключение Ошибка_Очереди. Этот синхронизированный интерфейс может быть реализован только типом задачи или защищённым типом, и таким образом обеспечивает безопасный конкурентный доступ.
Пример использования интерфейса:
тип Очередь_Быстрого_Обслуживания есть новый Очередь с записью ...;
процедура Добавить(О : вход выход Очередь_Быстрого_Обслуживания; Человек : вход Имя_Человека);
процедура Удалить_Первый(О : вход выход Очередь_Быстрого_Обслуживания;
Человек : выход Имя_Человека);
функция Текущий_Счёт(О : вход Очередь_Быстрого_Обслуживания) возвращает Натуральное_число;
функция Максимальный_Счёт(О : вход Очередь_Быстрого_Обслуживания) возвращает Натуральное_число;
процедура Добавить(О : вход выход Очередь_Быстрого_Обслуживания; Человек : вход Имя_Человека);
процедура Удалить_Первый(О : вход выход Очередь_Быстрого_Обслуживания;
Человек : выход Имя_Человека);
функция Текущий_Счёт(О : вход Очередь_Быстрого_Обслуживания) возвращает Натуральное_число;
функция Максимальный_Счёт(О : вход Очередь_Быстрого_Обслуживания) возвращает Натуральное_число;
...
Кассир, Счётчик : Очередь_Быстрого_Обслуживания;
...
-- Добавить Кейси (см. 3.10.1) в очередь кассира:
Добавить (Кассир, Кейси);
-- После оплаты, переместить Кейси в очередь счётчика:
Перенос (Кассир, Счётчик);
...
-- Добавить Кейси (см. 3.10.1) в очередь кассира:
Добавить (Кассир, Кейси);
-- После оплаты, переместить Кейси в очередь счётчика:
Перенос (Кассир, Счётчик);
...
Интерфейс, такой как Очередь, может быть использован непосредственно как родитель нового типа (как показано здесь), или может быть использован как предок, когда тип производный. В любом случае, примитивные операции интерфейса наследуются. Для очереди, реализация четырёх унаследованных процедур будет обязательно предоставлена. Внутри вызова Перенос, вызовы будут перенаправлены на реализации Добавить и Удалить_Первый для типа Очередь_Быстрого_Обслуживания.
Пример интерфейса задачи:
тип Устройство_Последовательной_Связи есть интерфейс задачи; -- см. 9.1
процедура Чтение (Устр : вход Устройство_Последовательной_Связи; С : выход Символ) есть абстрактный;
процедура Запись(Устр : вход Устройство_Последовательной_Связи; С : вход Символ) есть абстрактный;
процедура Чтение (Устр : вход Устройство_Последовательной_Связи; С : выход Символ) есть абстрактный;
процедура Запись(Устр : вход Устройство_Последовательной_Связи; С : вход Символ) есть абстрактный;
Интерфейс Устройство_Последовательной_Связи имеет две диспетчерские операции, которые предназначены для реализации входам задачи (см. 9.1).