Embarcadero Delphi
interfaces
Zoeken…
Opmerkingen
Interfaces worden gebruikt om de benodigde informatie en de verwachte output van methoden en klassen te beschrijven, zonder informatie over de expliciete implementatie.
Klassen kunnen interfaces implementeren en interfaces kunnen van elkaar erven . Als een klasse een interface implementeert , betekent dit dat alle functies en procedures die door de interface worden blootgesteld in de klasse aanwezig zijn.
Een speciaal aspect van interfaces in delphi is dat instanties van interfaces een levenslang beheer hebben op basis van referentietelling. De levensduur van klasseninstanties moet handmatig worden beheerd.
Gezien al deze aspecten kunnen interfaces worden gebruikt om verschillende doelen te bereiken:
- Bieden meerdere verschillende implementaties voor bewerkingen (bijvoorbeeld opslaan in een bestand, database of verzenden als e-mail, allemaal als interface "SaveData")
- Verminder afhankelijkheden, verbeter de ontkoppeling en maak de code beter onderhoudbaar en testbaar
- Werk met instanties in meerdere eenheden zonder u te bemoeien met levenslang beheer (hoewel zelfs hier valkuilen bestaan, pas op!)
Een interface definiëren en implementeren
Een interface wordt als een klasse gedeclareerd, maar zonder toegangsmodificatoren ( public
, private
, ...). Ook zijn geen definities toegestaan, dus variabelen en constanten kunnen niet worden gebruikt.
Interfaces moeten altijd een unieke identificatie hebben , die kan worden gegenereerd door op Ctrl + Shift + G te drukken.
IRepository = interface
['{AFCFCE96-2EC2-4AE4-8E23-D4C4FF6BBD01}']
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
Om een interface te implementeren, moet de naam van de interface achter de basisklasse worden toegevoegd. De klasse moet ook een afstammeling zijn van TInterfacedObject
(dit is belangrijk voor het levenslange beheer ).
TDatabaseRepository = class(TInterfacedObject, IRepository)
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
Wanneer een klasse een interface implementeert, moet deze alle methoden en functies bevatten die in de interface zijn gedeclareerd, anders compileert deze niet.
Een ding dat het vermelden waard is, is dat toegangsmodificatoren geen enkele invloed hebben als de beller met de interface werkt. Alle functies van de interface kunnen bijvoorbeeld worden geïmplementeerd als strict private
, maar kunnen nog steeds worden aangeroepen vanuit een andere klasse als een exemplaar van de interface wordt gebruikt.
Implementeren van meerdere interfaces
Klassen kunnen meer dan één interface implementeren, in tegenstelling tot het overerven van meer dan één klasse ( Multiple Inheritance ), wat niet mogelijk is voor Delphi-klassen. Om dit te bereiken, moet de naam van alle interfaces door komma's gescheiden achter de basisklasse worden toegevoegd.
Natuurlijk moet de implementatieklasse ook de functies definiëren die door elk van de interfaces worden aangegeven.
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;
Overerving voor interfaces
Interfaces kunnen van elkaar erven, precies zoals klassen dat ook doen. Een implementatieklasse moet dus functies van de interface en alle basisinterfaces implementeren. Op deze manier weet de compiler echter niet dat de implicerende klasse ook de basisinterface implementeert, hij kent alleen de interfaces die expliciet worden vermeld. Daarom zou het gebruik as ISuperInterface
op TImplementer
niet werken. Dat resulteert ook in de gangbare praktijk om ook expliciet alle TImplementer = class(TInterfacedObject, IDescendantInterface, ISuperInterface)
te implementeren (in dit geval 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;
Eigenschappen in interfaces
Aangezien de declaratie van variabelen in interfaces niet mogelijk is, kan de "snelle" manier property Value: TObject read FValue write FValue;
definiëren ( property Value: TObject read FValue write FValue;
) niet worden gebruikt. In plaats daarvan moeten de Getter en de setter (elk alleen indien nodig) ook in de interface worden gedeclareerd.
IInterface = interface(IInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
Een ding dat het vermelden waard is, is dat de uitvoerende klasse het onroerend goed niet hoeft aan te geven. De compiler accepteert deze code:
TImplementer = class(TInterfacedObject, IInterface)
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
end;
Een voorbehoud is echter dat op deze manier de eigenschap alleen toegankelijk is via een instantie van de interface, niet via de klasse zelf. Ook verhoogt het toevoegen van de eigenschap aan de klasse de leesbaarheid.