Programowanie w języku Delphi

Interfejsy

Interfejsy, podobnie jak klasy, służą do przechowywania funkcji i procedur. W odróżnieniu od klasy, interfejsy jedynie definiują zbiór funkcji i procedur, nie zawierają części implementacyjnej.

Interfejsy deklarowane są z użyciem słowa kluczowego interface:

type
  IFoo = Interface
    Procedure Bar;
  End;

Podobnie jak wszystkie klasy VCL, dziedziczą po TObject, wszystkie interfejsy dziedziczą po interfejsie IUnknown. Sama deklaracja interfejsu jest prosta, jednak to klasa musi implementować funkcje i procedury ów interfejsu:

type
  TFoo = Class(TObject, IFoo)
  Public
    Procedure Bar;
  End;
 
Procedure TFoo.Bar;
Begin
  WriteLn('Hello World!');
End;

Język Delphi nie umożliwia dziedziczenia po wielu klasach. Możliwe jest jednak implementowanie wielu interfejsów, których nazwy oddzielone są od siebie znakiem przecinka. Innymi słowy, klasa TFoo użyta w przykładzie musi zawierać procedurę Bar, która zadeklarowana została w interfejsie IFoo.


Ogólnie przyjęte zostało, że nazwy interfejsów poprzedza litera I.
Istnieje kilka istotnych reguł dotyczących interfejsów, które powinieneś znać:

  • Interfejsy mogą zawierać jedynie metody lub właściwości. Pola są zabronione.
  • Jako że interfejsy nie mogą zawierać pól, słowa kluczowe read i write muszą wskazywać na metody.
  • Wszystkie metody interfejsu są domyślnie publiczne. W interfejsach nie istnieje coś takiego jak dostępność klas.
  • Interfejsy nie mają konstruktorów ani destruktorów.
  • Metody interfejsu nie mogą być opatrzone dyrektywami virtual, dynamic, abstract lub override.
Spójrz jeszcze raz na powyższy kod Delphi. Próba kompilacji takiego kodu może zakończyć się komunikatem błędu: Undeclared identifier: 'QueryInterface', Undeclared identifier: '_AddRef', Undeclared identifier: '_Release'. Wymusza to na nas zaimplementowanie w interfejsie wymienionych metod.

Umieść kursor w dowolnym miejscu wewnątrz klasy i naciśnij kombinację klawiszy Ctrl + Spacja, aby wywołać mechanizm automatycznego wykańczania kodu. W wyświetlonym oknie wykańczania kodu zostaną na czerwono wyświetlone niezaimplementowane jeszcze metody. Zaznacz na liście wszystkie metody oznaczone kolorem czerwonym ? przytrzymując wciśnięty klawisz Shift użyj klawiszy strzałek lub myszki. Naciśnij klawisz Enter ? metody interfejsu zostaną automatycznie dodane do definicji klasy. Naciśnij kombinację klawiszy Ctrl + Shift + C, aby wykończyć część implementacyjną dla nowo dodanych metod:

type
  TFoo = class(TObject, IFoo)
  public
    procedure Bar;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
  end;

Jeśli nasza klasa implementuje wiele interfejsów, które zawierają metody oznaczone takimi samymi sygnaturami, musimy dla tych metod stworzyć odpowiednie aliasy. Spójrz na poniższy przykład:

Type
  IFoo = Interface
    Procedure Bar;
  End;
 
  IBar = Interface
    Procedure Bar;
  End;
 
  TFooBar = Class(TObject, IFoo, IBar)
  Public
    // Aliasy
    Procedure IFoo.Bar = IFooBar;
    Procedure IBar.Bar = IBarBar;
    // Metody
    Procedure IFooBar;
    Procedure IBarBar;
 
    Function _AddRef: Integer; stdcall;
    Function _Release: Integer; stdcall;
 
    Function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
 
  End;


Zobacz też:

4 komentarze

samueladama 2014-06-10 16:32

Mam to samo zdanie, art jest bezużyteczny, mój poprzednik nie napisał dlaczego, ja napiszę, otóż artykuł opisuje podstawy użycia interfejsów w delphi, jednakże po tym gdy go przeczytałem, zadałem sobie pytanie, po co używać interfejsów? Skoro klasy dają mi to samo, a nawet więcej? No i właśnie brakuje mi tu tej istotnej informacji.

marogo 2009-11-17 08:43

Bezużyteczny ten artykuł....

bighead 2008-10-15 22:37

Funkcje te słuzą zliczaniu referencji do obiektów. Chodzi o to by wiedziec czy gdzies trzymasz obiekt przypisany do interfejsu. np

var
 i : IFoo;
 Foobar: TFooBar
begin
 ....
 i:=Foobar; // Powoduje samoistne wywolanie _AddRef.
 i:=nil; // Powoduje samoistne wywolanie _Release. To samo osiagnie sie przy wyjsciu z procedury (gdy zmienna i bedzie usuwana) !

Generalnie ma to sens gdy masz liste interfejsów na ktorej przechowyjesz obiekty implementujace dany interfejs. Wtedy jak skasujesz wszystkie referencje do danego obiektu to on znika. Przyjrzyj sie pliką nagłowkowym do blibiotek DLL np do DirectX. Obiekt jest tworzony rpzez funkcje lub metode, a Ty dostajesz do niego interface, a jak chcesz goz wolnic to poprostu przypisujesz nil. ;) To użyteczne i dość wygodne rozwiazanie :)

A jezeli chodzi o implemetacje tych metod to standardowo wkleja sie taki kod :
function TParent.QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
begin
  if GetInterface(IID, Obj) then Result := 0 else Result := E_NOINTERFACE;
end;

function TParent._AddRef: Integer; stdcall;
begin
 Result := InterlockedIncrement(FRefCount);
end;

function TParent._Release: Integer; stdcall;
begin
  Result := InterlockedDecrement(FRefCount);
  if Result = 0 then Destroy;
end;

lub za klase bazowa w hierarchi dziedziczenia przyjmuje TInterfacedObject.

Widzialem ze czesc osob nieco przerabia ta idee dodajac do constructora _addRef, a zamiast destructora uzywajac _Release dzieki czemu obiekt znika dopiero na rzadanie, a nie po usunieciu ostatniej referencji.

Johny_Morfina 2008-06-03 11:47

mam takie pytanie:
czemu sluza funkcje:
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
    function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
i co powinienem w nich napisac?