Интерфейсы (определение интерфейса, GUID, примеры интерфейсов, реализация интерфейса).

При определении абстрактного класса, для того чтобы представить базовый класс иерархии, вы можете прийти в точку, в которой абстрактный класс является настолько абстрактным, что он лишь перечисляет ряд виртуальных функций, не обеспечивая их реализацию. Этот вид «чисто» абстрактного класса также может быть определен с помощью специальной технологии, именуемой interface (интерфейс). Ввиду этого мы обращаемся к этим классам как к интерфейсам.

Технически интерфейс – это не класс, а хотя и напоминает его. От класса его отличает то, что он воспринимается как полностью отдельный элемент с определенными характеристиками:

1) в отношении объектов типа interface ведется подсчет числа ссылок, и интерфейсы автоматически уничтожаются при отсутствии ссылок на них. Этот механизм подобен тому, как Delphi управляет длинными строками; это делает управление памятью практически автоматизированным;

2) класс может исходить от единственного базового класса, но может реализовывать множество интерфейсов;

3) так же, как и классы исходят от TObject, все интерфейсы исходят от IInterface, формируя полностью отдельную иерархию.

Важно подчеркнуть, что интерфейсы поддерживают менее ограниченную реализацию полиморфизма. Полиморфизм объектной ссылки основан на определенной ветви иерархии. Полиморфизм интерфейса работает по всей иерархии. Безусловно, интерфейсы придерживаются идеи инкапсуляции и обеспечивают более свободное соединение между классами, чем наследование. Синтаксис объявления интерфейса:

Type

ICanFly = interface

[‘{EAD9C4B4-E1C5-4CF4-9FA0-3B812C880A21}’]

functionFly: string;

end;

Этот интерфейс имеет глобально уникальный идентификатор (Globally Unique Identifier, GUID) – числовой идентификатор, имеющий определение, и основанный на Windows-соглашениях. Эти идентификаторы можно генерировать в редакторе Delphi нажатием сочетания клавиш Ctrl+Shift+G.

Совет: хотя интерфейс можно компилировать и использовать без указания GUID, последний обязательно придется сгенерировать, поскольку он требуется для запроса интерфейса или динамического приведения типов as с использованием типа этого интерфейса. Все преимущество интерфейсов заключается (обычно) в предоставлении гибкости во время выполнения; поэтому, в отличие от типов класса, интерфейсы без GUID не очень полезны.

После того как интерфейс объявлен, для его реализации можно определить класс:

Type

TAirplane = class(IInterfacedObject, ICanFly)

functionFly: string;

end;

RTL уже предоставляет несколько базовых классов для реализации базового поведения, требуемого интерфейсом IInterface. Для внутренних объектов используйте класс IInterfacedObject.

Методы интерфейса можно реализовать с помощью статических методов (как в предыдущем примере) или с помощью виртуальных методов. В классах-потомках с помощью директивы override можно подменить виртуальные методы. Если не использовать виртуальные методы, то в классе-наследнике все еще можно предоставить новую реализацию, повторно объявив тип интерфейса и повторно выполнив привязку методов интерфейса к новым версиям статических методов. На первый взгляд, использование виртуальных методов для реализации интерфейсов позволяет в классах-наследниках осуществить более «гладкое» кодирование, но оба подхода одинаково мощны и удобны. Однако использование виртуальных методов влияет на объем программного кода и использование памяти. Теперь, после того как определена реализация интерфейса, можно написать программный код, использующий объект посредством переменной соответствующего типа:

Var

Flyer1: ICanFly;

Begin

Flyer1 := TAirplane.Create;

Flyer.Fly;

End;

Как только вы присвоили объект переменной типа интерфейс, Delphi автоматически с помощью оператора as просматривает, реализует ли объект этот интерфейс. Можно явно выразить это действие следующим образом:

Flyer1 := TAirplane.Create as ICanFly;

Используете ли вы непосредственное присвоение или выражение as, среда Delphi делает одно и то же действие: она вызывает метод _AddRef данного объекта(объявленный IInterface). Стандартная реализация этого метода, как и реализация, предоставляемая IInterfacedObject, приводит к увеличению счетчика ссылок, проверяя, не имеет ли он значение «ноль», и в случае необходимости уничтожает объект. Вот поэтому в предыдущем примере нет программного кода, освобождающего ресурсы, используемые созданным объектом.

Иначе говоря, объекты, на которые ссылаются «интерфейсные» переменные, являются в Delphi переменными с подсчетом ссылок. Эти объекты автоматически высвобождают занимаемую ими память, когда на них нет ссылок «интерфейсных» переменных.