Interfaces
Les interfaces permettent de définir un contrat, au travers d'un ensembles de méthodes et propriétés, sans définir une implémentation. L'implémentation d'une interface sera réalisée au moyen de classe(s) pouvant n'avoir aucun lien de parenté entre elles. Ceci permet un découplage complet entre des interfaces et leur implémentation.
Il s'agit d'un type référence supportant des mécanismes d'abstraction et de virtualisation, les interfaces supportent l'héritage, il s'agit cependant d'un héritage de contrat, et non d'un héritage d'implémentation.
Les interfaces ont nécessairement une ou plusieurs classes comme support d'implémentation, une classe pouvant supporter simultanément plusieurs interfaces différentes non-aparentées.
Déclaration
Toutes les classes dérivent de l'interface racine IInterface, et peuvent être déclarées comme suit:
INomDeLInterface = interface (IInterfaceAncetre)
...membres de l'interface...
end;
Si l'interface ancêtre n'est pas précisée, il s'agira automatiquement de IInterface.
Une interface représentant un contrat, elle n'a ni implémentation ni champs, et uniquement des méthodes, elle peut cependant avoir des propriétés, qui sont alors utilisable comme sucre syntaxique des methoéde.
Par convention, les interfaces sont préfixées d'un "I" suivi d'une majuscule, comme tous les autres types.
Méthodes
Les méthodes peuvent être des function (retourne un résulat), procedure (ne retourne pas un résultat) ou method (retourne optionnellement un résultat).
Elles définissent le contrat de l'interface, un objet souhaitant implémenter une interface devra fournir une implémentation de chacune des méthodes de l'interface.
Propriétés
Les propriétés permettent d'exposer des propriétés d'un objet de manière encapsulée, elles peuvent être simples ou paramétrées:
property ProprieteParametree[parametres...] : TypePropriete [read Getter] [write Setter] [default];
Une proprieté à un Getter et un Setter optionels (au moins une des deux doit être définie). Une propriété ne définissant qu'un Getter sera dite en lecture seule, une propriété ne définissant qu'un Setter sera dit en écriture seule, et une propriété définissant les deux sera dite en lecture écriture.
Pour une propriété simple, le Getter doit être une méthode sans paramètre retournant un résultat du type approprié. Le Setter quand à lui devra être une méthode acceptant un paramètre unique du type de la propriété et ne retournant pas de résultat.
Pour une propriété paramétrée, les Getter/Setter devront être des méthodes, similairement à la propriété simple, mais acceptant les paramètres de la propriété. Une propriété paramétrée pourra être qualifiée avec default, dans ce cas elle sera accessible directement sur une instance de l'objet. Pour les expressions de Setter, la valeur affectée est accessible au travers de l'identificateur Value, elle peuvent correspondre soit à la partie gauche d'une affectation, soit à une instruction.
Opérateurs
- as permet d'obtenir une interface à partir d'une instance.
- implements permet de tester de manière dynamique si une classe supporte une interface donnée.
Exemple
procedure Hello;
end;
type IMyOtherInterface = interface
procedure World;
end;
type TTest = class (IMyInterface)
procedure Hello; begin PrintLn('Hello test'); end;
end;
type TWorld = class
procedure Hello; begin PrintLn('Hello world'); end;
end;
type TSubWorld = class (TWorld, IMyInterface, IMyOtherInterface)
procedure World; begin PrintLn('subworld'); end;
end;
var t := new TTest;
PrintLn(t implements IMyInterface); // True
PrintLn(t implements IMyOtherInterface); // False
var i := t as IMyInterface; // Hello test
i.Hello;
var s := new TSubWorld;
PrintLn(s implements IMyInterface); // True
PrintLn(s implements IMyOtherInterface); // True
i := s as IMyInterface;
var o := s as IMyOtherInterface;
i.Hello; // Hello world
o.World; // subworld