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 ISuperInterfaceTImplementer 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.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow