Suche…


Bemerkungen

Schnittstellen werden verwendet, um die benötigten Informationen und die erwartete Ausgabe von Methoden und Klassen zu beschreiben, ohne Informationen über die explizite Implementierung bereitzustellen.

Klassen können Schnittstellen implementieren , und Schnittstellen können voneinander erben . Wenn eine Klasse eine Schnittstelle implementiert , bedeutet dies, dass alle von der Schnittstelle bereitgestellten Funktionen und Prozeduren in der Klasse vorhanden sind.

Ein besonderer Aspekt von Interfaces in Delphi ist, dass Instanzen von Interfaces über ein Lebensdauermanagement verfügen, das auf der Referenzzählung basiert. Die Lebensdauer von Klasseninstanzen muss manuell verwaltet werden.

Unter Berücksichtigung all dieser Aspekte können Schnittstellen verwendet werden, um verschiedene Ziele zu erreichen:

  • Mehrere verschiedene Implementierungen für Operationen bereitstellen (z. B. Speichern in einer Datei, Datenbank oder Senden als E-Mail, alle als Schnittstelle "SaveData")
  • Reduzieren Sie Abhängigkeiten, verbessern Sie die Entkopplung und machen Sie so den Code besser wartbar und überprüfbar
  • Arbeiten Sie mit Instanzen in mehreren Einheiten, ohne sich durch das Lifetime-Management zu stören (obwohl auch hier Fallstricke vorhanden sind, passen Sie auf!)

Schnittstelle definieren und implementieren

Eine Schnittstelle wird wie eine Klasse deklariert, jedoch ohne Zugriffsmodifizierer ( public , private , ...). Außerdem sind keine Definitionen zulässig, daher können Variablen und Konstanten nicht verwendet werden.

Schnittstellen sollten immer über eine eindeutige Kennung verfügen, die durch Drücken von Strg + Umschalttaste + G generiert werden kann.

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

Um eine Schnittstelle zu implementieren, muss der Name der Schnittstelle hinter der Basisklasse hinzugefügt werden. Außerdem sollte die Klasse ein Nachkomme von TInterfacedObject (dies ist wichtig für die Lebenszeitverwaltung ).

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

Wenn eine Klasse eine Schnittstelle implementiert, muss sie alle in der Schnittstelle deklarierten Methoden und Funktionen enthalten. Andernfalls wird sie nicht kompiliert.

Bemerkenswert ist, dass Zugriffsmodifizierer keinen Einfluss haben, wenn der Anrufer mit der Schnittstelle arbeitet. Zum Beispiel können alle Funktionen der Schnittstelle als strict private Member implementiert werden, sie können jedoch auch von einer anderen Klasse aufgerufen werden, wenn eine Instanz der Schnittstelle verwendet wird.

Implementierung mehrerer Schnittstellen

Klassen können mehr als eine Schnittstelle implementieren, anstatt von mehreren Klassen zu erben ( Multiple Inheritance ), was für Delphi-Klassen nicht möglich ist. Um dies zu erreichen, muss der Name aller Schnittstellen durch Kommas getrennt hinter der Basisklasse hinzugefügt werden.

Natürlich muss die implementierende Klasse auch die von jeder der Schnittstellen deklarierten Funktionen definieren.

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;

Vererbung für Schnittstellen

Schnittstellen können voneinander erben, genau wie Klassen auch. Eine implementierende Klasse muss also Funktionen der Schnittstelle und aller Basisschnittstellen implementieren. Auf diese Weise weiß der Compiler jedoch nicht, dass die implizierende Klasse auch die Basisschnittstelle implementiert. Er kennt nur die explizit aufgeführten Schnittstellen. Aus diesem Grund würde die Verwendung as ISuperInterface auf TImplementer nicht funktionieren. Dies führt in der üblichen Praxis auch dazu, alle Basisschnittstellen explizit zu implementieren (in diesem Fall 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;

Eigenschaften in Schnittstellen

Da die Deklaration von Variablen in Schnittstellen nicht möglich ist, kann die "schnelle" Art der Definition von Eigenschaften ( property Value: TObject read FValue write FValue; ) nicht verwendet werden. Stattdessen müssen auch Getter und Setter (jeweils nur bei Bedarf) in der Schnittstelle deklariert werden.

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

    property Value: TObject read GetValue write SetValue;
end;

Bemerkenswert ist, dass die implementierende Klasse die Eigenschaft nicht deklarieren muss. Der Compiler würde diesen Code akzeptieren:

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

Ein Nachteil ist jedoch, dass auf diese Eigenschaft nur über eine Instanz der Schnittstelle zugegriffen werden kann, nicht über die Klasse selbst. Durch das Hinzufügen der Eigenschaft zur Klasse wird außerdem die Lesbarkeit erhöht.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow