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

Pensez à cliquer sur
Réponse acceptée lorsque la réponse vous convient.
May Delphi be with you