пятница, 30 августа 2013 г.

Интерфейсы объектов

Перевод раздела Object Interfaces (Delphi) из справочной системы Delphi

Интерфейс объекта (или просто интерфейс) определяет методы, которые могут быть реализованы классом. Объявление интерфейсов схоже с объявлением классов, но их отличие в том, что вы не можете создавать экземпляры интерфейса и, кроме того, они не могут иметь собственных объявлений методов. Реализация методов интерфейса – это скорее ответственность класса, который поддерживает интерфейс. Переменная типа интерфейс может хранить ссылку на объект, класс которого реализует этот интерфейс. Но через эту переменную вы сможете обратиться только к методам, которые объявлены в интерфейсе.


Интерфейсы дают некоторые преимущества множественного наследования, позволяя при этом избежать семантических трудностей. Они также являются неотъемлемой частью моделей распределенных объектов (таких как SOAP). Применяя такую модель, объекты, поддерживающие интерфейсы, могут взаимодействовать с объектами, разработанными на C++, Java и других языках.



Интерфейсные типы

Интерфейсы, как и классы, могут быть объявлены только во внешней области видимости программы или модуля. Они не могут объявляться внутри процедуры или функции. Объявление интерфейсного типа имеет вид:

type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;
Предупреждение: ancestorInterface и спецификация GUID обязательны для поддержки совместимости с Win32 COM. Если доступ к интерфейсу должен выполняться через COM, - необходимо определить ancestorInterface и GUID.

Во многих отношениях объявления интерфейсов схожи с объявлениями классов, но содержат следующие ограничения:

  • memberList может содержать только методы и свойства. Объявление полей в интерфейсах запрещено.
  • По причине отсутствия полей спецификаторы свойств read и write должны содержать методы.
  • Все компоненты (members) интерфейса имеют видимость public. Спецификаторы видимости и хранения (visibility and storage specifiers) запрещены. (Свойство-массив может быть объявлено как свойство по умолчанию.)
  • Интерфейсы не имеют конструкторов или деструкторов. Вы не можете создавать экземпляров интерфейса кроме как при помощи классов, которые их реализуют.
  • Методы могут быть объявлены как virtual, dynamic, abstract или override. Поскольку интерфейсы не содержат реализации собственных методов, эти обозначения не имеют значения.

Далее приведен пример объявления интерфейса:

type IMalloc = interface(IInterface)
    ['{00000002-0000-0000-C000-000000000046}'] 
    function Alloc(Size: Integer): Pointer; stdcall; 
    function Realloc(P: Pointer; Size: Integer): Pointer; stdcall; 
    procedure Free(P: Pointer); stdcall;
    function GetSize(P: Pointer): Integer; stdcall;
    function DidAlloc(P: Pointer): Integer; stdcall;
    procedure HeapMinimize; stdcall;
  end;

В некоторых объявлениях интерфейсов зарезервированное слово interface заменяется на dispinterface.



IInterface и наследование

Интерфейс, как и класс, наследует методы своего предка. Но в отличии от классов, интерфейсы не реализуют методов. То, что наследует интерфейс, - это обязательство реализации методов, обязательство, которое передается классам, поддерживающим интерфейс.

Объявление интерфейса определяет интерфейс-предок. Если такового не указано, интерфейс будет прямым потомком IInterface, определенном в модуле System и являющимся основным предком всех интерфейсов. На платформе Win32, интерфейс IInterface объявляет три метода: QueryInterface, _AddRef, и _Release.

Замечание: IInterface эквивалентен IUnknown. Для платформо-независимых приложений следует использовать Iinterface, а IUnknown – для специфических программ, зависящих от Win32.

QueryInterface предоставляет возможность получения ссылки на интерфейсы, которые поддерживает объект. _AddRef и _Release обеспечивают управление памятью при работе интерфейса. Самый простой способ реализовать эти методы – это наследовать класс от TinterfacedObject, объявленном в модуле System. Также существует возможность обойтись без этих методов, реализуя их как пустые функции. Однако для COM объектов управление должно осуществляться через _AddRef и _Release.

Предупреждение: QueryInterface, _AddRef и _Release необходимы для поддержки Win32 COM совместимости. Если доступ к вашему интерфейсу должен выполняться через COM, эти методы должны быть реализованы.

Идентификация интерфейсов и GUID

Объявление интерфейса может содержать глобальный уникальный идентификатор(globally unique identifier - GUID), который представляет собой строковый литерал, предшествующий список компонентов и заключенный в скобки. GUID имеет следующий вид:

 ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']

Где каждый x – это шестнадцатеричная цифра (от 0 до 9 или от A до F). Редактор библиотеки типов (Type Library editor) автоматически генерирует для создаваемых интерфейсов. Кроме того, вы можете сгенерировать GUID самостоятельно, нажав комбинацию Ctrl+Shift+G в редакторе кода.

GUID – это 16-байтовое двоичное значение, которое идентифицирует интерфейс. Если у интерфейса есть GUID, вы сможете получить ссылки на реализации интерфейса при помощи механизма запросов интерфейса.

Замечание: GUID используются только для совместимости с COM.

Типы TGUID и PGUID, объявленные в модуле System, применяются управления GUID.

 type 
   PGUID = ^TGUID;
   TGUID = packed record D1: Longword; 
                         D2: Word;
                         D3: Word;
                         D4: array[0..7] of Byte; 
   end;
Замечание: модуль SysUtils содержит перегружаемую функцию с именем Supports, которая возвращает true или false в зависимости от того, поддерживает ли класс или экземпляр класса определенный интерфейс, идентифицируемый GUID. Функция Supports используется как операторы Delphi is и as, с единственным значимым отличием – функция Supports принимает за правый операнд либо GUID, либо интерфейсный тип, связанный с GUID, в то время как is и as принимают имя типа.

Функция Supports может вызываться двумя способами:

if Supports(Allocator, IMalloc) then ...

или:

if Supports(Allocator, IID_IMalloc) then ...

Конвенции вызова для интерфейсов

Конвенция вызова по умолчанию для методов интерфейсов - register, но интерфейсы, используемые в разных модулях (а особенно, если они написаны на разных языках), должны объявлять все методы с конвенцией stdcall. На платформе Win32 вы можете использовать конвенцию safecall для реализации методов двойных интерфейсов.


Свойства интерфейсов

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

Поскольку в объявлении интерфейса запрещены поля, спецификаторы свойств read и write должны содержать методы.


Отложенные объявления

Объявление интерфейса, которое заканчивается зарезервированным словом interface, за которым следует точка с запятой(;), без указания предка, GUID или списка компонентов, называется отложенным объявлением. Такое объявление должно быть разрешено в текущей секции объявления типов объявлением такого же интерфейса. Другими словами, между отложенным и окончательным объявлением не должно появиться ничего, кроме объявления других типов.

Отложенные объявления позволяют создавать взаимозависимые интерфейсы. Например:

 type 
   IControl = interface; 
   IWindow = interface 
       ['{00000115-0000-0000-C000-000000000044}'] 
       function GetControl(Index: Integer): IControl; 
         //. . . 
     end; 
   IControl = interface 
       ['{00000115-0000-0000-C000-000000000049}'] 
       function GetWindow: IWindow; 
       //. . . 
     end;

Взаимонаследуемые интерфейсы запрещены. То есть нельзя создать интерфейс Iwindow, наследуемый от IControl и наследовать IControl от IWindow.

Комментариев нет:

Отправить комментарий