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

Реализация интерфейсов

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

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




Объявления классов

Такое объявление имеет вид:

type className = class (ancestorClass, interface1, ..., interfaceN)
 memberList
end;

Следующий пример

type
  TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
    // ...
  end;

объявляет класс с именем TmemoryManager, который поддерживает интерфейсы IMalloc и IErrorInfo. Когда класс поддерживает интерфейс, он должен реализовывать (или наследовать реализацию) каждого метода, объявленного в интерфейсе.

Далее приведено объявление TInterfacedObject в модуле System (для платформы Win32):

type
 TInterfacedObject = class(TObject, IInterface)
 protected
   FRefCount: Integer;
   function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
   function _AddRef: Integer; stdcall;
   function _Release: Integer; stdcall;
 public
   procedure AfterConstruction; override;
   procedure BeforeDestruction; override;
   class function NewInstance: TObject; override;
   property RefCount: Integer read FRefCount;
 end;

TInterfacedObject поддерживает интерфейс Iinterface, то есть TInterfacedObject объявляет и реализует все три метода IInterface.

Классы, которые поддерживают интерфейсы, также могут быть использованы в качестве базовых классов (в первом примере объявлен класс TmemoryManager, являющийся прямым наследником TInterfacedObject.) На платформе Win32 каждый интерфейс наследуется от IInterface, а класс, поддерживающий интерфейсы, должен реализовывать методы QueryInterface, _AddRef, и _Release. TinterfacedObject, объявленный в модуле System, реализует эти методы, а значит он является базовым классом, подходящим для наследования классов, поддерживающих интерфейсы.

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


Раздел сопоставления методов

Вы можете перекрыть порядок сопоставления методов, включив в объявление класса раздел сопоставления методов. Когда класс поддерживает два и более интерфейса с одинаковыми именами методов, этот раздел поможет избежать конфликтов имен.

Раздел сопоставления имен имеет вид:

procedure interface.interfaceMethod = implementingMethod;

или:

function interface.interfaceMethod = implementingMethod;

где implementingMethod это метод, объявленный в классе или его предках. Сам implementingMethod может быть объявлен позже в объявлении класса, но не может относиться к классу-предку, объявленному в другом модуле и иметь область видимости private.

Например, объявление класса:

type
  TMemoryManager = class(TInterfacedObject, IMalloc, IErrorInfo)
    function IMalloc.Alloc = Allocate;
    procedure IMalloc.Free = Deallocate;
 // ...
  end;

сопоставляет методы Alloc и Free интерфейса Imalloc с методами Allocate и Deallocate в классе TMemoryManager.

Раздел сопоставления методов не может изменять сопоставление методов, указанное в классе-предке.


Изменение наследуемой реализации

Классы-потомки могут изменять реализацию методов, перекрывая ее. Для этого необходимо, чтобы реализующие методы были динамическими или виртуальными.

Класс может повторно реализовать наследуемый от класса-предка интерфейс целиком. Для этого потребуется повторно указать имя интерфейса в объявлении класса-потомка. Например:

type
  IWindow = interface
    ['{00000115-0000-0000-C000-000000000146}']
    procedure Draw;
    // ...
  end;
  TWindow = class(TInterfacedObject, IWindow)
    // TWindow реализует pocedure Draw интерфейса IWindow ;
    // ...
  end;
  TFrameWindow = class(TWindow, IWindow)
    // TFrameWindow повторно реализует procedure Draw интерфейса IWindow ;
    // ...
  end;
  

Повторная реализация скрывает наследуемую реализацию этого интерфейса. То есть раздел сопоставления методов в классе-предке перестает действовать при перекрытии реализации интерфейса.


Реализация интерфейсов путем делегирования (только для Win32)

На платформе Win32 директива implements позволяет делегировать реализацию интерфейса свойству в реализующем классе. В примере

property MyInterface: IMyInterface read FMyInterface implements IMyInterface;

объявлено свойство с именем MyInterface , которое реализует интерфейс IMyInterface.

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

  • Должно иметь тип класса или интерфейса.
  • Не может быть свойством-массивом или иметь спецификатор индекса.
  • Должно иметь спецификатор read. Если свойство имеет метод для чтения, он должен быть объявлен с конвенцией вызова по умолчанию register и не может быть динамическим (тем не менее, он может быть виртуальным), а его объявление не может содержать директиву message.

Класс, который вы используете для реализации делегируемого интерфейса, должен наследоваться от TAggregationObject.



Делегирование свойству интерфейсного типа (только для Win32)

Если делегируемое свойство имеет тип интерфейса, этот интерфейс, или интерфейс, от которого он наследуется, должен быть упомянут в списке предков класса, в котором объявляется это свойство. Делегируемое свойство должно возвращать объект, класс которого полностью реализует интерфейс, указанный в директиве implements. При этом его объявление не должно содержать раздел переопределения методов. Например:

type
  IMyInterface = interface
    procedure P1;
    procedure P2;
  end;
  TMyClass = class(TObject, IMyInterface)
    FMyInterface: IMyInterface;
    property MyInterface: IMyInterface read FMyInterface implements IMyInterface;
  end;
var
  MyClass: TMyClass;
  MyInterface: IMyInterface;
begin
  MyClass := TMyClass.Create;
  MyClass.FMyInterface := ...// какой-либо объект, класс которого поддерживает IMyInterface
  MyInterface := MyClass;
  MyInterface.P1;
end;

Делегирование свойству типа класс (только для Win32)

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

Таким образом, существует возможность реализовать одни методы в классе, указанном в объявлении свойства, а остальные – в классе, содержащем свойство. Разделы переопределения методов можно использовать как обычно для разрешения неопределенностей или для указания конкретного метода. Интерфейс не может быть реализован более, чем одним свойством типа класс. Например:

type
  IMyInterface = interface
    procedure P1;
    procedure P2;
  end;
  TMyImplClass = class
    procedure P1;
    procedure P2;
  end;
  TMyClass = class(TInterfacedObject, IMyInterface)
    FMyImplClass: TMyImplClass;
    property MyImplClass: TMyImplClass read FMyImplClass implements IMyInterface;
    procedure IMyInterface.P1 = MyP1;
    procedure MyP1;
  end;
procedure TMyImplClass.P1;
     // ...
procedure TMyImplClass.P2;
     // ...
procedure TMyClass.MyP1;
     // ...
var
  MyClass: TMyClass;
  MyInterface: IMyInterface;
begin
  MyClass := TMyClass.Create;
  MyClass.FMyImplClass := TMyImplClass.Create;
  MyInterface := MyClass;
  MyInterface.P1; // вызывает TMyClass.MyP1;
  MyInterface.P2; // вызывает TImplClass.P2;
end;

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

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