Szukaj…


Uwagi

Interfejsy służą do opisywania potrzebnych informacji oraz oczekiwanych wyników metod i klas, bez dostarczania informacji o jawnej implementacji.

Klasy mogą implementować interfejsy, a interfejsy mogą dziedziczyć od siebie. Jeśli klasa implementuje interfejs, oznacza to, że wszystkie funkcje i procedury udostępnione przez interfejs istnieją w klasie.

Szczególnym aspektem interfejsów w delphi jest to, że instancje interfejsów mają zarządzanie przez całe życie w oparciu o liczenie referencji. Cykl życia instancji klasy musi być zarządzany ręcznie.

Biorąc pod uwagę wszystkie te aspekty, interfejsy można wykorzystać do osiągnięcia różnych celów:

  • Zapewnij wiele różnych implementacji operacji (np. Zapisywanie w pliku, bazie danych lub wysyłanie jako e-mail, wszystko jako interfejs „SaveData”)
  • Zmniejsz zależności, poprawiając odsprzęganie, dzięki czemu kod będzie łatwiejszy w konserwacji i testowaniu
  • Pracuj z instancjami w wielu jednostkach bez kłopotów związanych z zarządzaniem przez całe życie (choć nawet tutaj istnieją pułapki, uważaj!)

Definiowanie i implementacja interfejsu

Interfejs jest zadeklarowany jak klasa, ale bez modyfikatorów dostępu ( public , private , ...). Ponadto nie są dozwolone żadne definicje, więc nie można używać zmiennych i stałych.

Interfejsy powinny zawsze mieć unikalny identyfikator , który można wygenerować, naciskając Ctrl + Shift + G.

IRepository = interface
    ['{AFCFCE96-2EC2-4AE4-8E23-D4C4FF6BBD01}']
    function  SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;

Aby zaimplementować interfejs, należy dodać nazwę interfejsu za klasą podstawową. Ponadto klasa powinna być potomkiem TInterfacedObject (jest to ważne dla zarządzania przez całe życie ).

TDatabaseRepository = class(TInterfacedObject, IRepository)
    function  SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;

Kiedy klasa implementuje interfejs, musi zawierać wszystkie metody i funkcje zadeklarowane w interfejsie, w przeciwnym razie nie zostanie skompilowana.

Warto zauważyć, że modyfikatory dostępu nie mają żadnego wpływu, jeśli program wywołujący działa z interfejsem. Na przykład wszystkie funkcje interfejsu można zaimplementować jako strict private członków, ale nadal można je wywoływać z innej klasy, jeśli używana jest instancja interfejsu.

Implementowanie wielu interfejsów

Klasy mogą implementować więcej niż jeden interfejs, w przeciwieństwie do dziedziczenia z więcej niż jednej klasy ( wielokrotne dziedziczenie ), co nie jest możliwe w przypadku klas Delphi. Aby to osiągnąć, nazwa wszystkich interfejsów musi być oddzielona przecinkami za klasą bazową.

Oczywiście klasa implementująca musi również zdefiniować funkcje zadeklarowane przez każdy z interfejsów.

IInterface1 = interface
    ['{A2437023-7606-4551-8D5A-1709212254AF}']
    procedure Method1();
    function Method2(): Boolean;
end;

IInterface2 = interface
    ['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
    procedure SetValue(const aValue: TObject);
    function  GetValue(): TObject;

    property Value: TObject read GetValue write SetValue;
end;

TImplementer = class(TInterfacedObject, IInterface1, IInterface2)
    // IInterface1
    procedure Method1();
    function Method2(): Boolean;

    // IInterface2
    procedure SetValue(const aValue: TObject);
    function  GetValue(): TObject

    property Value: TObject read GetValue write SetValue;
end;

Dziedziczenie interfejsów

Interfejsy mogą dziedziczyć od siebie, podobnie jak klasy. Klasa implementująca musi zatem implementować funkcje interfejsu i wszystkich interfejsów bazowych. W ten sposób jednak kompilator nie wie, że klasa implementująca implementuje również interfejs podstawowy, zna tylko interfejsy, które są wyraźnie wymienione. Dlatego używanie as ISuperInterface na TImplementer nie działa. Powoduje to również powszechną praktykę, aby jawnie implementować również wszystkie interfejsy podstawowe (w tym przypadku TImplementer = class(TInterfacedObject, IDescendantInterface, ISuperInterface) ).

ISuperInterface = interface
    ['{A2437023-7606-4551-8D5A-1709212254AF}']
    procedure Method1();
    function Method2(): Boolean;
end;

IDescendantInterface = interface(ISuperInterface)
    ['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
    procedure SetValue(const aValue: TObject);
    function  GetValue(): TObject;

    property Value: TObject read GetValue write SetValue;
end;

TImplementer = class(TInterfacedObject, IDescendantInterface)
    // ISuperInterface
    procedure Method1();
    function Method2(): Boolean;

    // IDescendantInterface
    procedure SetValue(const aValue: TObject);
    function  GetValue(): TObject

    property Value: TObject read GetValue write SetValue;
end;

Właściwości w interfejsach

Ponieważ deklaracja zmiennych w interfejsach nie jest możliwa, nie można użyć „szybkiego” sposobu definiowania właściwości ( property Value: TObject read FValue write FValue; ). Zamiast tego w interfejsie należy zadeklarować również moduł pobierający i ustawiający (każdy tylko w razie potrzeby).

IInterface = interface(IInterface)
    ['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
    procedure SetValue(const aValue: TObject);
    function  GetValue(): TObject;

    property Value: TObject read GetValue write SetValue;
end;

Warto zauważyć, że klasa implementująca nie musi deklarować właściwości. Kompilator zaakceptuje ten kod:

TImplementer = class(TInterfacedObject, IInterface)
    procedure SetValue(const aValue: TObject);
    function  GetValue(): TObject
end;

Jednak jednym zastrzeżeniem jest to, że w ten sposób można uzyskać dostęp do właściwości tylko poprzez instancję interfejsu, a nie przez samą klasę. Ponadto dodanie właściwości do klasy zwiększa czytelność.



Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow