Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
[ Монахов ] Объектно-ориентированное программирование.doc
Скачиваний:
94
Добавлен:
16.08.2013
Размер:
490.5 Кб
Скачать

6. Интерфейсы и множественное наследование

6.1. Общие представления об интерфейсах в Object pascal

Интерфейс объекта определяют методы и свойства, которые могут быть реализованы классом-наследником этого интерфейса. Они описываются аналогично абстрактным классам, так же, как абстрактные классы, но в отличие от них, не могут иметь экземпляров, не могут иметь реализации никаких своих методов (в Object PASCAL допускается реализация части методов в абстрактные классы). Реализация методов интерфейсов осуществляется в классе, поддерживающем (наследующем) данный интерфейс. Переменная типа интерфейс - это ссылка. Она дает возможность ссылаться на объект, чей класс реализует данный интерфейс. Однако с помощью такой переменной разрешается вызывать только методы, декларированные в данном интерфейсе, а не любые методы данного объекта. Интерфейсы являются альтернативой множественному наследованию, имеют практически все его достоинства и лишены его недостатков. Их использование существенно для написания ПО для распределенных систем на основе COM (the Component Object Model) и CORBA (Common Object Request Broker Architecture). Объекты, поддерживающие интерфейсы, могут взаимодействовать с COM- объектами, написанными на C++ или Java.

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

type

interfaceName =

interface (ancestorInterface)

['{GUID}']

список полей

end;

Отличия от классов:

  • Не бывает экземпляров, реализующих тип интерфейс.

  • Список полей интерфейса может включать только свойства и методы. Поля данных использовать нельзя. Соответственно, спецификаторы read и write должны быть методами.

  • Все поля интерфейса всегда имеют тип видимости public (без явного указания); не разрешено использовать спецификаторов видимости (но у массивов может быть использован спецификатор default).

  • Не бывает конструкторов и деструкторов.

  • Методы не могут быть специфицированы как виртуальные, динамические, перекрытые, абстрактные. Поскольку они не имеют реализации в экземплярах типа, эти различия не имеют значения.

  • Наследование через интерфейсы множественное.

  • Реализация интерфейса может быть только в классе, при этом он должен реализовать все методы интерфейса (а значит, и все свойства)

Интерфейс, как и класс, наследует все методы прародителя, однако только на уровне абстракций, без реализации методов. Однако интерфейс наследует право реализации этих методов в классе, поддерживающем этот интерфейс. В декларации интерфейса можно указать, что интерфейс наследуется от прародительского интерфейса. Если такого указания нет, то интерфейс является непосредственным потомком IUnknown, который определен в модуле System и является прародителем для всех интерфейсов. В IUnknown продекларировано 3 метода — QueryInterface, _AddRef, Release. QueryInterface предназначен для поддержки интерфейсов объектов. _AddRef, _Release обеспечивают управление ссылками на интерфейсы. Простейший путь реализовать эти методы — создать класс-наследник от tInterfacedObject , описанного в модуле System.

Пример описания интерфейса:

type

IMalloc =

interface(IUnknown)

['{00000002-0000-0000-C000-000000000046}']

function Alloc(Size: Integer): Pointer; stdcall;

functionRealloc(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;

Перед использованием продекларированного интерфейса он должен быть реализован в классе. Реализация осуществляется с помощью декларации в списке прародителей класса:

type

className =

class (ancestorClass,interface1,...,interfaceN)

memberList

end;

Например,

type

TMemoryManager =

class(TInterfacedObject, IMalloc, IErrorInfo)

...

end;

Когда класс реализует интерфейс, он должен реализовать (или наследовать реализацию) каждого метода, декларированного в интерфейсе. Ниже приведено описание tInterfacedObject из модуля System:

type

TInterfacedObject =

class(TObject, IUnknown)

protected

FRefCount: Integer;

function QueryInterface(const IID: TGUID; out Obj): Integer; stdcall;

function _AddRef: Integer; stdcall;

function _Release: Integer; stdcall;

public

property RefCount: Integer read FRefCount;

end;

tInterfacedObject реализует интерфейс. Поэтому в нем описаны и реализованы все три метода IUnknown. Классы, реализующие интерфейсы, могут быть использованы как прародители. Класс tInterfacedObject реализует три метода интерфейса IUnknown и поэтому удобен как прародитель для всех классов, реализующих интерфейсы. Когда интерфейс реализован в классе, каждый из его методов реализован соответствующим методом (по умолчанию с тем же именем) и с такой же сигнатурой.

В классе-наследнике можно перекрыть методы реализуемого интерфейса. При этом соответствующий метод должен быть виртуальным или динамическим). Также возможно заново унаследовать интерфейс:

type

IWindow =

interface

['{00000115-0000-0000-C000-000000000146}']

procedure Draw;

...

end;

TWindow =

class(TInterfacedObject, IWindow) //TWindow реализует IWindow

procedure Draw;

...

end;

TFrameWindow =

class(TWindow, IWindow) //TFrameWindow переопределяет реализацию Iwindow

procedure Draw;

...

end;

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

Соседние файлы в предмете Информатика