Embarcadero Delphi
gränssnitt
Sök…
Anmärkningar
Gränssnitt används för att beskriva den nödvändiga informationen och den förväntade produktionen av metoder och klasser utan att ge information om den exakta implementeringen.
Klasser kan implementera gränssnitt och gränssnitt kan ärva från varandra. Om en klass implementerar ett gränssnitt betyder det att alla funktioner och procedurer som exponeras av gränssnittet finns i klassen.
En speciell aspekt av gränssnitt i delphi är att instanser av gränssnitt har en livslång hantering baserad på referensräkning. Klassinstansers livslängd måste hanteras manuellt.
Med tanke på alla dessa aspekter kan gränssnitt användas för att uppnå olika mål:
- Tillhandahålla flera olika implementationer för operationer (t.ex. att spara i en fil, databas eller skicka som e-post, allt som gränssnitt "SaveData")
- Minska beroenden, förbättra frikopplingen och därmed göra koden bättre underhållbar och testbar
- Arbeta med instanser i flera enheter utan att bli oroliga av livstidshantering (även om det finns fallgropar, se upp!)
Definiera och implementera ett gränssnitt
Ett gränssnitt deklareras som en klass, men utan åtkomstmodifierare ( public
, private
, ...). Inga definitioner är tillåtna, så variabler och konstanter kan inte användas.
Gränssnitt ska alltid ha en unik identifierare , som kan genereras genom att trycka på Ctrl + Shift + G.
IRepository = interface
['{AFCFCE96-2EC2-4AE4-8E23-D4C4FF6BBD01}']
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
För att implementera ett gränssnitt måste gränssnittsnamnet läggas till bakom basklassen. Klassen bör också vara en ättling till TInterfacedObject
(detta är viktigt för livstidshanteringen ).
TDatabaseRepository = class(TInterfacedObject, IRepository)
function SaveKeyValuePair(aKey: Integer; aValue: string): Boolean;
end;
När en klass implementerar ett gränssnitt måste det innehålla alla metoder och funktioner som deklareras i gränssnittet, annars kommer den inte att kompilera.
En sak som är värt att notera är att åtkomstmodifierare inte har något inflytande, om den som ringer arbetar med gränssnittet. Till exempel kan alla funktioner i gränssnittet implementeras som strict private
medlemmar, men kan fortfarande ringas från en annan klass om en instans av gränssnittet används.
Implementera flera gränssnitt
Klasser kan implementera mer än ett gränssnitt, i motsats till att ärva från mer än en klass ( Multiple ärft ), vilket inte är möjligt för Delphi-klasser. För att uppnå detta måste namnet på alla gränssnitt läggas till komma-separerade bakom basklassen.
Naturligtvis måste implementeringsklassen också definiera de funktioner som deklareras av vart och ett av gränssnitten.
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;
Arv för gränssnitt
Gränssnitt kan ärva från varandra, precis som klasser också. En implementeringsklass måste således implementera funktioner i gränssnittet och alla basgränssnitt. På detta sätt vet kompilatorn emellertid inte att implantatklassen också implementerar basgränssnittet, den känner bara till gränssnitten som uttryckligen listas. Det är därför att användning as ISuperInterface
på TImplementer
skulle fungera. Det resulterar också i den vanliga praxisen att också uttryckligen implementera alla basgränssnitt (i detta 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;
Egenskaper i gränssnitt
Eftersom deklarationen av variabler i gränssnitt inte är möjlig, kan det "snabba" sättet att definiera properites ( property Value: TObject read FValue write FValue;
) inte användas. Istället måste även Getter och setter (var och en endast om det behövs) deklareras i gränssnittet.
IInterface = interface(IInterface)
['{6C47FF48-3943-4B53-8D5D-537F4A0DEC0D}']
procedure SetValue(const aValue: TObject);
function GetValue(): TObject;
property Value: TObject read GetValue write SetValue;
end;
En sak som är värt att notera är att den genomförande klassen inte behöver deklarera fastigheten. Kompilatorn accepterar denna kod:
TImplementer = class(TInterfacedObject, IInterface)
procedure SetValue(const aValue: TObject);
function GetValue(): TObject
end;
En varning är dock att det här sättet bara kan nås via en instans av gränssnittet, inte genom klassen själv. Att lägga till egenskapen till klassen ökar också läsbarheten.