Interfaces d'objets (Object Pascal)

De Appmethod Topics
Aller à : navigation, rechercher

Remonter à Interfaces d'objets - Index


Une interface d'objet, ou plus simplement une interface, définit des méthodes qui peuvent être implémentées par une classe. Les interfaces sont déclarées comme des classes mais elles ne peuvent être instanciées directement et n'ont pas leurs propres définitions de méthode. C'est aux classes supportant une interface qu'est laissée la responsabilité de fournir les implémentations pour la méthode de l'interface. Une variable de type interface peut référencer un objet dont la classe implémente cette interface. Cependant, en utilisant cette variable, il n'est possible d'appeler que les méthodes déclarées dans l'interface.

Les interfaces proposent certains des avantages de l'héritage multiple sans en avoir la complexité sémantique. Elles jouent également un rôle essentiel dans l'utilisation des modèles d'objets distribués (comme SOAP). En utilisant un modèle d'objets distribués, les objets personnalisés qui supportent les interfaces peuvent interagir avec des objets écrits en C++, en Java et dans d'autres langages.

Types interface

Comme les classes, les interfaces ne peuvent se déclarer que dans la portée la plus extérieure d'un programme ou d'une unité, mais pas dans une procédure ou une fonction. La déclaration d'un type interface a la forme suivante :

 type interfaceName = interface (ancestorInterface) ['{GUID}'] memberList end;

Avertissement : Le ancestorInterface et la spécification GUID sont nécessaires pour supporter l'interopérabilité COM Win32. Si votre interface doit être accessible via COM, assurez-vous de spécifier le ancestorInterface et le GUID.

Par bien des côtés, la déclaration d'une interface ressemble à celle d'une classe, mais certaines restrictions s'appliquent :

  • Le memberList ne peut contenir que des méthodes et des propriétés. Les champs ne sont pas autorisés dans les interfaces.
  • Une interface n'ayant pas de champ, les spécificateurs de propriété read et write doivent être des méthodes.
  • Tous les membres d'une interface sont publics. Les spécificateurs de visibilité et de stockage ne sont pas autorisés. (Mais une propriété tableau peut être déclarée default.)
  • Les interfaces n'ont ni constructeurs, ni destructeurs. Elles ne peuvent être instanciées, si ce n'est via des classes qui implémentent leurs méthodes.
  • Il n'est pas possible de déclarer des méthodes comme étant virtual, dynamic, abstract ou override. Comme les interfaces n'implémentent pas leur propres méthodes, ces directives sont dépourvues de sens.

Voici un exemple de déclaration d'interface :

type IMalloc = interface(IInterface)
    ['{00000002-0000-0000-C000-000000000046}'] 
    function Alloc(Size: Integer): Pointer; stdcall; 
    function Realloc(P: Pointer; Size: Integer): Pointer; stdcall; 
    procedure Free(P: Pointer); stdcall;
    function GetSize(P: Pointer): Integer; stdcall;
    function DidAlloc(P: Pointer): Integer; stdcall;
    procedure HeapMinimize; stdcall;
  end;

Dans certaines déclarations d'interfaces, le mot réservé interface est remplacé par dispinterface.

IInterface et héritage

Comme une classe, une interface hérite de toutes les méthodes de ces ancêtres. Mais les interfaces, à la différence des classes, n'implémentent pas les méthodes. L'interface hérite de l'obligation d'implémenter les méthodes, une obligation qui est en fait supportée par toute classe gérant l'interface.

La déclaration d'une interface peut spécifier une interface ancêtre. Si l'ancêtre n'est pas spécifié, l'interface descend directement de IInterface, qui est définie dans l'unité System et qui est l'ancêtre ultime de toutes les autres interfaces. Sur Win32, IInterface déclare trois méthodes : QueryInterface, _AddRef et _Release.

Remarque : IInterface est équivalent à IUnknown. Vous devez généralement utiliser IInterface pour les applications indépendantes des plates-formes et réserver l'usage de IUnknown aux programmes spécifiques qui incluent des dépendances Win32.

QueryInterface permet d'obtenir une référence aux différentes interfaces gérées par un objet. _AddRef et _Release proposent la gestion de la durée de vie des références d'interface. Le moyen le plus simple d'implémenter ces méthodes consiste à dériver la classe d'implémentation à partir de la classe TInterfacedObject définie dans l'unité System. Il est également possible de se passer de l'une quelconque de ces méthodes en l'implémentant en tant que fonction vide ; cependant, les objets COM doivent être gérés via _AddRef et _Release.

Avertissement : QueryInterface, _AddRef et _Release sont nécessaires pour supporter l'interopérabilité COM Win32. Si votre interface doit être accessible via COM, assurez-vous d'implémenter ces méthodes.

Identification de l'interface et les GUID

La déclaration d'une interface peut spécifier un identificateur globalement unique (GUID) représenté par un littéral chaîne placé entre crochets juste avant la liste des membres. La partie GUID de la déclaration doit avoir la forme suivante :

 ['{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}']

où chaque x est un chiffre hexadécimal (de 0 à 9 et de A à F). L'éditeur de bibliothèque de types génère automatiquement des GUID pour les nouvelles interfaces. Vous pouvez également générer des GUID en appuyant sur Ctrl+Maj+G dans l'éditeur de code.

Un GUID est une valeur binaire sur 16 octets qui identifie une interface de manière unique. Si une interface a un GUID, vous pouvez utiliser l'interrogation d'interface pour obtenir des références à ses implémentations.

Remarque : Les GUID ne sont utilisés que pour l'interopérabilité COM.

Les types TGUID et PGUID, déclarés dans l'unité System, sont utilisés pour manipuler des GUID.

 type 
     PGUID = ^TGUID;
     TGUID = packed record
       D1: Cardinal;
       D2: Word;
       D3: Word;
       D4: array[0..7] of Byte;
   end;

La fonction Supports peut être appelée de deux manières :

if Supports(Allocator, IMalloc) then ...

ou :

if Supports(Allocator, IID_IMalloc) then ...

Remarque : L'unité SysUtils fournit une fonction surchargée, appelée Supports, qui renvoie true ou false quand les instances et les types de classes supportent une interface particulière représentée par un GUID. La fonction Supports est utilisée comme les opérateurs is et as de Object Pascal. La différence significative est que la fonction Supports peut prendre en opérande droit un GUID ou un type d'interface associé à un GUID, tandis que is et as prennent le nom d'un type. Pour de plus amples informations sur is et as, voir Références de classes.

Conventions d'appel pour les interfaces

La convention d'appel par défaut pour les méthodes d'interface est register mais les interfaces partagées par plusieurs modules (en particulier si ces modules sont écrits dans des langages différents) doivent déclarer toutes leurs méthodes avec la convention stdcall. Sur Win32, vous pouvez utiliser safecall pour implémenter les méthodes des interfaces doubles.

Propriétés d'interfaces

Les propriétés déclarées dans une interface ne sont accessibles que dans des expressions de type interface : il n'est pas possible d'y accéder via des variables de type classe. De plus, les propriétés d'interfaces ne sont visibles que dans les programmes où l'interface est compilée.

Dans une interface, les spécificateurs read et write d'une propriété doivent être des méthodes puisque les champs ne sont pas disponibles.

Déclarations avancées

La déclaration d'une interface qui se termine par le mot réservé interface et un point-virgule, sans spécifier un ancêtre, le GUID ni la liste des membres est une déclaration avancée. Une déclaration avancée doit être complétée par une déclaration de définition de la même interface à l'intérieur de la même section de déclaration de types. En d'autres termes, entre une déclaration avancée et sa déclaration de définition, il ne peut y avoir que d'autres déclarations de type.

Les déclarations avancées permettent de déclarer des interfaces mutuellement dépendantes. Par exemple :

 type 
   IControl = interface; 
   IWindow = interface 
       ['{00000115-0000-0000-C000-000000000044}'] 
       function GetControl(Index: Integer): IControl; 
         //. . . 
     end; 
   IControl = interface 
       ['{00000115-0000-0000-C000-000000000049}'] 
       function GetWindow: IWindow; 
       //. . . 
     end;

Par contre, les interfaces dérivées mutuellement ne sont pas autorisées. Ainsi, il n'est pas permis de dériver IWindow de IControl et de dériver également IControl de IWindow.

Voir aussi