(Niveau Élevé) POO- Comment en Delphi faire une classe qui hérite de deux autres

Signaler
Messages postés
32
Date d'inscription
vendredi 14 février 2003
Statut
Membre
Dernière intervention
2 décembre 2009
-
cs_mounjetado
Messages postés
66
Date d'inscription
lundi 13 mars 2006
Statut
Membre
Dernière intervention
4 août 2008
-
Salut,
peux-t-on en Delphi (tout comme en C++) faire une classe qui dérive de deux autres classes.

Pour faire cà facile au début:
Par exemple, j'ai une classe ITEM, qui contient les propriétés LONGUEUR et LARGEUR, puis elle peut contenir des descendants comme: 1- la classe ITEM-ROULEAU qui contient en plus une propriété DIAMETRE ou 2- un autre Descendant nommé ITEM-FEUILLE qui lui possède en plus de Longueur et Largeur une propriété HAUTEUR.

Donc nous nous retrouvons en résumé avec

ITEM = class(?)
property Longueur
property Largeur

ITEM-ROULEAU = Class(ITEM)
property Diametre

ITEM-FEUILLE = Class(ITEM)
property Hauteur

Là où sa se complique c'est que ITEM, je veux qu'il dérive dans certains un cas de la Classe ENTETE-COMMANDE qui lui possède la propriété NO-ORDER, et dans un autre Cas la classe ITEM dérive de la classe FAVORI-CLIENT qui lui possède la propriété NO-CLIENT, ADRESSE, TEL.

Donc sa ressemble à:

ENTETE-COMMANDE = class(?)
property NO-ORDER

FAVORI-CLIENT = Class(?)
property NO-CLIENT
property ADRESSE
property TEL

et

ITEM = CLass(ENTETE-COMMANDE , FAVORI-CLIENT) ????
property Longueur
property Largeur

Comment implanter ITEM. Je crois qu'on peut s'en sortir avec les interfaces, mais comment? J'en sais rien..
_______
Bonus

6 réponses

Messages postés
4580
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
25
Précision importante : le Pascal Objet n'autorise pas l'héritage multiple. Nous verrons plus loin qu'il n'est d'ailleurs pas le seul langage dans ce cas.
En revanche, on peut y parvenir indirectement en utilisant les interfaces. Cette solution est bien plus élégante, propre et moins risquée que l'héritage multiple qui est un nid à problème avec le C++.

A la lecture de la première partie des questions, tu affirmes que Item "peut contenir des descendants". Je ne vois pas comment une classe mère peut contenir des classes filles qui sont ses héritières...Qui fût le premier : la poule ou l'oeuf ? voilà une question à laquelle personne en peur répondre et qui reste donc non résolue à ce jour.
Or, dans l'esquisse de déclaration que tu donnes en dessous, on voit que les classes Item_Rouleau et Item_Feuille descendent purement et simplement de la classe Item. Nous retombons bien sur le principe de l'héritage simple. Je ne pense pas qu'il soit nécessaire d'expliquer davantage ce concept, sinon je te renvoie à la littérature abondante traitant de ce sujet.

Venons en à la deuxième partie (la plus intéressante à mon goût).
Pour la suite des explications, j'utilise la convention de notation de Borland, à savoir :
- chaque nom de classe est précédé de la lettre T pour désigner un type
- chaque nom d'interface est précédé de la lettre I pour indiquer une interface.
Ces quelques principes peuvent éviter bien des confusions dans le code par la suite quand il faudra instancier des objets.

Je vais reprendre les mots que tu as employés et les analyser.
je veux qu'il dérive dans certains un cas de la Classe ENTETE-COMMANDE et et dans un autre Cas la classe ITEM dérive de la classe FAVORI-CLIENT.
Cela veut donc dire que tantôt un objet de la classe TItem devra se comporter comme s'il était du type TEnteteCommande et tantôt comme s'il était du type TFavoriClient.
Voilà donc un candidat parfait pour l'utilisation des interfaces !

Pour bien comprendre les interfaces, il faut les imaginer comme des contrats type, décrivant les méthodes (sous entendu des comportements) que les classes qui s'engageront à les utiliser devront respecter à la lettre.
Prenons l'exemple de l'en-tête de commande :
  IOrderHeader = interface
    function GetNoOrder: integer;
    procedure SetNoOrder(Value: integer);
  end;


Les classes qui signeront le contrat avec cette interface IOrderHeader devront donc implémenter deux méthodes. Comme on peut le deviner en lisant les noms de ces 2 méthodes, elles serviront à accèder à la propriété NoOrder. Comme rien n'était précisé dans l'exposé du problème, j'ai supposé que ces propriétés seraient en lecture+écriture.

Passons à la déclaration de la classe implémentant les méthodes GetNoOrder et SetNoOrder.
ATTENTION : dans les déclarations des classes ci-dessous, je n'ai pas mentionné les champs destinés à stocker les valeurs des propriétés pour ne pas alourdir davantage la présentation.
  TOrderHeader = class(TInterfacedObject, IOrderHeader)
    public
      //méthodes de l'interface IOrderHeader
      function GetNoOrder: integer;
      procedure SetNoOrder(Value: integer);
    
      property NoOrder: integer read GetNoOrder write SetNoOrder;
  end;


Par souci de réutilisabilité, je fais hériter la classe TOrderHeader de la classe TInterfacedObject de manière à ne pas avoir à réimplémenter les méthode _AddRef, _Release et QueryInterface déclarées dans l'interface IUnknown (IUnKnown est aux interfaces ce que la classe TObject est aux classes).

Pratiquons selon la même démarche en ce qui concerne la classe nommée TFavoriClient :
  IFavoriClient = interface
    function GetNoClient: integer;
    function GetAdresse: string;
    function GetTel: string;

    procedure SetNoClient(Value: integer);
    procedure SetAdresse(Value: string);
    procedure SetTel(Value: string);
  end;

  TFavoriClient = class(TInterfacedObject, IFavoriClient)
    public
      //méthodes de l'interface IFavoriClient
      function GetNoClient: integer;
      function GetAdresse: string;
      function GetTel: string;

      procedure SetNoClient(Value: integer);
      procedure SetAdresse(Value: string);
      procedure SetTel(Value: string);
    
      property NoClient: integer read GetNoClient write SetNoClient;
      property Adresse: string read GetAdresse write SetAdresse;
      property Tel: string read GetTel write SetTel;
  end;

Nous voilà donc avec deux classes implémentant deux interfaces différentes.
Alors, comment faire pour en obtenir une troisième qui aura la qualité des deux autres ?
C'est simple :
  TItem = class(TInterfacedObject, IOrderHeader, IFavoriClient)
    private
      //méthodes de l'interface IOrderHeader
      function GetNoOrder: integer;
      procedure SetNoOrder(Value: integer);
      //Méthodes de l'interface IFavoriClient
      function GetNoClient: integer;
      function GetAdresse: string;
      function GetTel: string;

      procedure SetNoClient(Value: integer);
      procedure SetAdresse(Value: string);
      procedure SetTel(Value: string);
    public

  end;


La classe TItem va implémenter les méthodes des interfaces IOrderHeader et IFavoriClient et respecte le contrat de départ signé avec celles-ci.

Comme dernière précision, je dirais qu'une interface peut être vue comme un pointeur sur une table de méthodes.
Déclarons l'usage d'interfaces :
var
  //attention à bien déclarer des interfaces et NON des classes !
  Order: IItem;
  Favori: IItem;
  //explications plus loin...
  CodeCommande: integer;
  AdresseClient: string;


Instancions ensuite un objet Item qui se comportera comme une en-tête de commande :
  //Instanciation 
  Order:= TItem.Create as IOrderHeader;

on utilise cette interface comme si de rien n'était :
  CodeCommande := Order.GetNoOrder;
  //changement de cap !
  Favori:=  Order as IFavoriClient;
  AdresseClient := Favori.GetAdresse; 


Je vais arrêter là les explications. Il y a tellement de choses à dire sur ce sujet.
Si tu es parvenu à lire jusqu'ici, alors je t'adresse mes félicitations...pour ta patience.
Expliquer la technique d'utilisation des interfaces en quelques lignes est impossible. Si tu souhaites aller plus loin, je t'invite à te tourner vers des ouvrages techniques. Ce ne sera pas du temps perdu. D'autres langages comme Java, PHP (depuis la version 5) utilisent cette technique extrêmement puissante et bien moins scabreuse que l'héritage multiple auquel tous les langages ont renoncé sauf un : le C++.
Il doit bien y avoir une raison... :big)

Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
32
Date d'inscription
vendredi 14 février 2003
Statut
Membre
Dernière intervention
2 décembre 2009

Salut,

et oui, je suis parvenu à tout lire le texte du début à la fin.... faut dire que le sujet m'intéressait beaucoup... Pour ce qui est des interface et des classes, je connaissais déjà la plupart des concepts et des nomenclatures... Mais c'était essentiellement l'héritage multiple sur lequel je m'interrogeais... Je savais que sa se faisait par interface et que surment Borland avait fait de quoi de simple, mais me restait seulement à savoir comment... By the way, encore une fois merci de ton temps et de tes précisions méticuleuses...

Merci

_______
Bonus
Messages postés
32
Date d'inscription
vendredi 14 février 2003
Statut
Membre
Dernière intervention
2 décembre 2009

Salut,

mon information est un peu incomplète.

La classe IITEM n'est pas déclaré ci-haut. Doit-elle dérivé à la fois de IOrderHeader et de IFavoriClient?

Lorsque je fais la ligne suivante:
Order:= TItem.Create as IOrderHeader

d'où
Order: IItem;

le compilateur indique une erreur:
Incompatible type 'TItem' and 'IOrderHeader'

Comment l'interface IItem peut -elle dérivé de deux autres interfaces? C'est quoi l'autre solution, si je présume que il y en a efectivement une autre? Il me manque un petit bout pour résoudre le casse-tête. Seulement la déclaration de IItem.

Merci de votre patience

_______
Bonus
Messages postés
32
Date d'inscription
vendredi 14 février 2003
Statut
Membre
Dernière intervention
2 décembre 2009

Bon et bien,

pour me répondre à moi meme.. je crois que sa se fait pas en delphi 7. Avec .Net oui, à ce que la littérature dit, mais de l'héritage multiple avec Delphi 7, la littérature semble prétendre que non, à moins qu'on me prouve le contraire.

Le seul moyen de s'en sortir, c'est donc de faire de l'aggréation,
par exemple:

type
IOrderHeader = interface
[etc..]
end;

TOrderHeader = class(TObject, IOrderHeader)
FMyItem: IItem;
property FMyItem: IItem read FMyItem implements IItem;
end;

var
MyClass: TOrderHeader ;
MyInterface: IItem;
begin
MyClass := TMyClass.Create;
MyClass.FMyInterface := ...
// some object whose class implements IItem

MyInterface := MyClass;
MyInterface.ProcedureEtc..;
end;

Donc c'est cà!
_______
Bonus
Messages postés
4580
Date d'inscription
samedi 19 janvier 2002
Statut
Modérateur
Dernière intervention
9 janvier 2013
25
Tiens, voici un bon tutorial sur les interfaces situé sur le site de Michel BARDOU.

Pourquoi tiens-tu absolument à cette notion d'héritage multiple ?
Je suis très surpris que la littérature mentionne la possibilité de l'héritage multiple avec .Net. Voici ce que dit un extrait de la documentation livrée avec le .Net framework SDK à la rubrique "héritage multiple":
"À la différence des autres types, qui sont dérivés uniquement d'un type de base unique, une interface peut être dérivée de plusieurs interfaces de base. Dès lors, une interface peut hériter de membres de type de même nom à partir de différentes interfaces de base. Dans ce cas, le nom hérité plusieurs fois n'est pas disponible dans l'interface dérivée, et toute référence à l'un de ces membres de type par l'intermédiaire de l'interface dérivée provoque une erreur de compilation, peu importe les signatures ou la surcharge. Au lieu de cela, les membres de type en conflit doivent être référencés via un nom d'interface de base."

Pensez à cliquer sur Réponse acceptée lorsque la réponse vous convient.
Messages postés
66
Date d'inscription
lundi 13 mars 2006
Statut
Membre
Dernière intervention
4 août 2008

j'aurais une question à propos de l'utilisation d'une interface.
si on a plusieurs projets ds un groupe de projets, dt certains sont des appli console et d'autres des appli VCL avec GUI, 
         peut-on partager des propriétés ou des données via plusieurs interfaces (par exemple, une interface permet l'accès en lecture, une autre en écriture), ou est-ce que les-dites propriétés sont propres à une interface?
         quelle est la visibilité d'une instance d'interface?
         peut-on instancier un objet d'une classe faisant référence à plusieurs interfaces dans l'appli principale, pour ensuite utiliser les instances d'interfaces chacune dans une appli console différente, tout en partageant des propriétés ou des données, ainsi que leur accès évidemment?

<hr />si Delphi m'était conté...