
- •Динамически загружаемые библиотеки
- •5.1. Динамически загружаемые библиотеки
- •5.2. Разработка библиотеки
- •5.2.1. Структура библиотеки
- •5.2.2. Экспорт подпрограмм
- •5.2.3. Соглашения о вызове подпрограмм
- •5.3. Использование библиотеки в программе
- •5.3.1. Статический импорт
- •5.3.2. Модуль импорта
- •5.3.3. Динамический импорт
- •5.3.4. Глобальные переменные и константы
- •5.3.5. Инициализация и завершение работы библиотеки
- •5.3.6. Исключительные ситуации и ошибки выполнения подпрограмм
- •5.3.7. Общий менеджер памяти
- •5.3.8. Стандартные системные переменные
- •Программирование на языке Delphi
- •6.1. Понятие интерфейса
- •6.2. Описание интерфейса
- •6.3. Расширение интерфейса
- •6.4. Глобально-уникальный идентификатор интерфейса
- •6.5. Реализация интерфейса
- •6.6. Использование интерфейса
- •6.7. Реализация нескольких интерфейсов
- •6.8. Реализация интерфейса несколькими классами
- •6.9. Связывание методов интерфейса с методами класса
- •6.10. Реализация интерфейса вложенным объектом
- •6.11. Совместимость интерфейсов
- •6.12. Совместимость класса и интерфейса
- •6.13. Получение интерфейса через другой интерфейс
- •6.14. Механизм подсчета ссылок
- •6.15. Представление интерфейса в памяти
- •6.16. Применение интерфейса для доступа к объекту dll-библиотеки
- •6.17. Итоги
- •7.1. Проект
- •7.1.1. Понятие проекта
- •7.1.2. Файлы описания форм
- •7.1.3. Файлы программных модулей
- •7.1.4. Главный файл проекта
- •7.1.5. Другие файлы проекта
- •7.2. Управление проектом
- •7.2.1. Создание, сохранение и открытие проекта
- •7.2.2. Окно управления проектом
- •7.2.3. Группы проектов
- •7.2.4. Настройка параметров проекта
- •7.2.5. Компиляция и сборка проекта
- •7.2.6. Запуск готового приложения
- •7.3. Форма
- •7.3.1. Понятие формы
- •7.3.2. Имя и заголовок формы
- •7.3.3. Стиль формы
- •7.3.4. Размеры и местоположение формы на экране
- •7.3.5. Цвет рабочей области формы
- •7.3.6. Рамка формы
- •7.3.7. Значок формы
- •7.3.8. Невидимая форма
- •7.3.9. Прозрачная форма
- •7.3.10. Полупрозрачная форма
- •7.3.11. Недоступная форма
- •7.3.12. События формы
- •7.4. Несколько форм в приложении
- •7.4.1. Добавление новой формы в проект
- •7.4.2. Добавление новой формы из Хранилища Объектов
- •7.4.3. Переключение между формами во время проектирования
- •7.4.4. Выбор главной формы приложения
- •7.4.5. Вызов формы из программы
- •7.5. Компоненты
- •7.5.1. Понятие компонента
- •7.5.2. Визуальные и невизуальные компоненты
- •7.5.3. «Оконные» и «графические» компоненты
- •7.5.4. Общие свойства визуальных компонентов
- •7.5.5. Общие события визуальных компонентов
- •7.6. Управление компонентами при проектировании
- •7.6.1. Помещение компонентов на форму и их удаление
- •7.6.2. Выделение компонентов на форме
- •7.6.3. Перемещение и изменение размеров компонента
- •7.6.4. Выравнивание компонентов на форме
- •7.6.5. Использование Буфера обмена
- •7.7. Закулисные объекты приложения
- •7.7.1. Application — главный объект, управляющий приложением
- •7.7.2. Screen — объект, управляющий экраном
- •7.7.3. Mouse — объект, представляющий мышь
- •7.7.4. Printer — объект, управляющий принтером
- •7.7.5. Clipboard — объект, управляющий Буфером обмена
- •7.8. Итоги
6.9. Связывание методов интерфейса с методами класса
Метод интерфейса связывается с методом класса по имени. Если имена по каким-то причинам не совпадают, то можно связать методы явно с помощью специальной конструкции языка Delphi.
Например, в классе TTextReader добавлены методы Next и Finished для поддержки интерфейса IStringIterator. Согласитесь, что существование в одном классе методов Next и NextLine вносит путаницу. По названию метода Next не понятно, что для этого метода является следующим элементом. Поэтому уточним название метода в классе TTextReader и воспользуемся явным связыванием методов, чтобы сохранить имя Next в интерфейсе IStringIterator:
type TTextReader = class(TInterfacedObject, ITextReader, IStringIterator) ... function NextItem: string; function IStringIterator.Next := NextItem; // Явное связывание end; |
При работе с объектами класса TTextReader через интерфейс IStringIterator вызов метода Next приводит к вызову метода NextItem:
var Obj: TTextReader; Intf: IStringIterator; begin ... Intf := Obj; Intf.Next; // -> Obj.NextItem; ... end; |
Очевидно, что связываемые методы должны совпадать по сигнатуре (списку параметров и типу возвращаемого значения).
6.10. Реализация интерфейса вложенным объектом
Случается, что реализация интерфейса содержится во вложенном объекте класса. Тогда не требуется программировать реализацию интерфейса путем замыкания каждого метода интерфеса на соответствующий метод вложенного объекта. Достаточно делегировать реализацию интерфейса вложенному объекту с помощью директивы implements:
type TTextParser = class(TInterfacedObject, ITextReader) ... FTextReader: ITextReader; property TextReader: ITextReader read FTextReader implements ITextReader; ... end; |
В этом примере интерфейс ITextReader в классе TTextParser реализуется не самим классом, а его внутренней переменной FTextReader.
Очевидно, что внутренний объект должен быть совместим с реализуемым интерфейсом.
6.11. Совместимость интерфейсов
Совместимость интерфейсов подчиняется определенным правилам. Если интерфейс создан расширением уже существующего интерфейса:
type IExtendedTextReader = interface(ITextReader) ... end; |
то интерфейсной переменной базового типа может быть присвоено значение интерфейсной переменной производного типа:
var Reader: ITextReader; ExtReader: IExtendedTextReader; begin ... Reader := ExtReader; // Правильно ... end; |
Но не наоборот:
ExtReader := Reader; // Ошибка! |
Правило совместимости интерфейсов чаще всего применяется при передаче параметров в процедуры и функции. Например, если процедура работает с переменными типа ITextReader,
procedure LoadFrom(const R: ITextReader); |
то ей можно передать переменную типа IExtendedTextReader:
LoadFrom(ExtReader); |
Заметим, что любая интерфейсная переменная совместима с типом данных IInterface — прародителем всех интерфейсов.
6.12. Совместимость класса и интерфейса
Интерфейсной переменной можно присвоить значение объектной переменной при условии, что объект (точнее его класс) реализует упомянутый интерфейс:
var Intf: ITextReader; // интерфейсная переменная Obj: TTextReader; // объектная переменная begin ... Intf := Obj; // В переменную Intf копируется ссылка на объект Obj ... end; |
Такая совместимость сохраняется в производных классах. Если класс реализует некоторый интерфейс, то и все его производные классы совместимы с этим интерфейсом (см. рисунок 6.3):
type TTextReader = class(TInterfacedObject, ITextReader) ... end;
TDelimitedReader = class(TTextReader) ... end;
var Intf: ITextReader; // интерфейсная переменная Obj: TDelimitedReader; // объектная переменная begin ... Intf := Obj; ... end; |
Рисунок
6.3. Классы TTextReader, TDelimitedReader и TFixedReader
совместимы с интерфейсом ITextReader
Однако, если класс реализует производный интерфейс, то это совсем не означает, что он совместим с базовым интерфейсом (см. рисунок 6.4):
type ITextReader = interface(IInterface) ... end;
IExtendedTextReader = interface(ITextReader) ... end;
TExtendedTextReader = class(TInterfacedObject, IExtendedTextReader) ... end;
var Obj: TExtendedTextReader; Intf: ITextReader; begin ... Intf := Obj; // Ошибка! Класс TExtendedTextReader не реализует // интерфейс ITextReader. ... end; |
Рисунок
6.4. Класс TExtendedTextReader совместим лишь с
интерфейсом IExtendedTextReader
Для совместимости с базовым интерфейсом нужно реализовать этот интерфейс явно:
type TExtendedTextReader = class(TInterfacedObject, ITextReader, IExtendedTextReader) ... end; |
Теперь класс TExtendedTextReader совместим и с интерфейсом ITextReader, поэтому следующее присваивание корректно:
Intf := Obj; |
Исключением из только что описанного правила является совместимость всех снабженных интерфейсами объектов с интерфейсом IInterface:
var Obj: TExtendedTextReader; Intf: IInterface; begin ... Intf := Obj; // Правильно, IInterface – особый интерфейс. ... end; |