🔝 Retour au Sommaire
L'héritage et le polymorphisme sont deux concepts fondamentaux de la programmation orientée objet. Ils permettent de créer des hiérarchies de classes, de réutiliser du code et de créer des programmes plus flexibles et maintenables.
L'héritage est un mécanisme qui permet à une classe (appelée classe dérivée ou classe fille) de récupérer automatiquement les propriétés et méthodes d'une autre classe (appelée classe de base ou classe parent).
Imaginez la classification des véhicules :
- Tous les véhicules ont des roues, un moteur, peuvent démarrer et s'arrêter
- Une voiture est un véhicule (elle hérite de ces caractéristiques)
- Un camion est aussi un véhicule (il hérite aussi de ces caractéristiques)
- Mais une voiture et un camion ont aussi leurs propres spécificités
type
// Classe de base
TVehicule = class
private
FMarque: string;
FVitesse: Integer;
public
procedure Demarrer;
procedure Arreter;
property Marque: string read FMarque write FMarque;
property Vitesse: Integer read FVitesse;
end;
// Classe dérivée
TVoiture = class(TVehicule) // TVoiture hérite de TVehicule
private
FNombrePortes: Integer;
public
procedure Klaxonner;
property NombrePortes: Integer read FNombrePortes write FNombrePortes;
end;La syntaxe class(TVehicule) indique que TVoiture hérite de TVehicule.
procedure TVehicule.Demarrer;
begin
ShowMessage('Le véhicule ' + FMarque + ' démarre');
FVitesse := 0;
end;
procedure TVehicule.Arreter;
begin
FVitesse := 0;
ShowMessage('Le véhicule s''arrête');
end;
procedure TVoiture.Klaxonner;
begin
ShowMessage('Bip bip !');
end;var
MaVoiture: TVoiture;
begin
MaVoiture := TVoiture.Create;
try
// Propriétés héritées de TVehicule
MaVoiture.Marque := 'Peugeot';
// Propriété propre à TVoiture
MaVoiture.NombrePortes := 5;
// Méthodes héritées de TVehicule
MaVoiture.Demarrer;
MaVoiture.Arreter;
// Méthode propre à TVoiture
MaVoiture.Klaxonner;
finally
MaVoiture.Free;
end;
end;- Réutilisation du code : pas besoin de réécrire le code commun
- Organisation logique : structure hiérarchique claire
- Maintenance facilitée : modifications dans la classe de base bénéficient à toutes les classes dérivées
- Extensibilité : facile d'ajouter de nouvelles classes dérivées
On peut créer plusieurs niveaux d'héritage :
type
// Niveau 1 : Classe de base
TAnimal = class
private
FNom: string;
FAge: Integer;
public
procedure SeNourrir;
property Nom: string read FNom write FNom;
property Age: Integer read FAge write FAge;
end;
// Niveau 2 : Classe dérivée de TAnimal
TMammifere = class(TAnimal)
private
FTypePoil: string;
public
procedure Allaiter;
property TypePoil: string read FTypePoil write FTypePoil;
end;
// Niveau 3 : Classe dérivée de TMammifere
TChien = class(TMammifere)
private
FRace: string;
public
procedure Aboyer;
property Race: string read FRace write FRace;
end;
// Niveau 3 : Autre classe dérivée de TMammifere
TChat = class(TMammifere)
public
procedure Miauler;
end;Dans cet exemple :
TChienhérite deTMammiferequi hérite deTAnimalTChiena accès à toutes les propriétés et méthodes deTMammifereetTAnimalTChatetTChienpartagent les caractéristiques communes deTMammifere
Le mot-clé inherited permet d'appeler la méthode de la classe parent :
type
TVehicule = class
public
constructor Create;
procedure Demarrer; virtual;
end;
TVoiture = class(TVehicule)
public
constructor Create;
procedure Demarrer; override;
end;
constructor TVehicule.Create;
begin
inherited Create; // Appelle le constructeur de TObject
ShowMessage('Création d''un véhicule');
end;
constructor TVoiture.Create;
begin
inherited Create; // Appelle le constructeur de TVehicule
ShowMessage('Création d''une voiture');
end;
procedure TVehicule.Demarrer;
begin
ShowMessage('Le véhicule démarre');
end;
procedure TVoiture.Demarrer;
begin
inherited Demarrer; // Appelle d'abord la méthode de TVehicule
ShowMessage('Mise en route du système de la voiture');
end;Le polymorphisme (du grec "plusieurs formes") est la capacité d'un objet à prendre plusieurs formes. En programmation, cela signifie qu'une variable de type classe parent peut contenir un objet de n'importe quelle classe dérivée.
Pour activer le polymorphisme, on utilise les mots-clés virtual et override :
type
TAnimal = class
public
procedure EmettreSon; virtual; // Méthode virtuelle
end;
TChien = class(TAnimal)
public
procedure EmettreSon; override; // Redéfinition
end;
TChat = class(TAnimal)
public
procedure EmettreSon; override; // Redéfinition
end;
TVache = class(TAnimal)
public
procedure EmettreSon; override; // Redéfinition
end;
// Implémentation
procedure TAnimal.EmettreSon;
begin
ShowMessage('L''animal fait un son');
end;
procedure TChien.EmettreSon;
begin
ShowMessage('Wouf wouf !');
end;
procedure TChat.EmettreSon;
begin
ShowMessage('Miaou !');
end;
procedure TVache.EmettreSon;
begin
ShowMessage('Meuh !');
end;Voici la magie du polymorphisme :
procedure FaireCrier(UnAnimal: TAnimal);
begin
// La méthode appelée dépend du type réel de l'objet
UnAnimal.EmettreSon;
end;
var
MonChien: TChien;
MonChat: TChat;
MaVache: TVache;
begin
MonChien := TChien.Create;
MonChat := TChat.Create;
MaVache := TVache.Create;
try
// Même procédure, comportements différents !
FaireCrier(MonChien); // Affiche "Wouf wouf !"
FaireCrier(MonChat); // Affiche "Miaou !"
FaireCrier(MaVache); // Affiche "Meuh !"
finally
MonChien.Free;
MonChat.Free;
MaVache.Free;
end;
end;Le polymorphisme brille vraiment quand on traite des collections :
var
Animaux: TList<TAnimal>;
Animal: TAnimal;
begin
Animaux := TList<TAnimal>.Create;
try
// Ajouter différents types d'animaux
Animaux.Add(TChien.Create);
Animaux.Add(TChat.Create);
Animaux.Add(TVache.Create);
Animaux.Add(TChien.Create);
// Faire crier tous les animaux
for Animal in Animaux do
Animal.EmettreSon; // Chaque animal fait son propre son !
finally
// Libérer tous les animaux
for Animal in Animaux do
Animal.Free;
Animaux.Free;
end;
end;Une méthode abstraite est une méthode qui DOIT être redéfinie dans les classes dérivées. Elle n'a pas d'implémentation dans la classe de base.
type
TForme = class
protected
FCouleur: string;
public
function CalculerSurface: Double; virtual; abstract;
function CalculerPerimetre: Double; virtual; abstract;
procedure Dessiner; virtual; abstract;
property Couleur: string read FCouleur write FCouleur;
end;
TRectangle = class(TForme)
private
FLargeur: Double;
FHauteur: Double;
public
constructor Create(ALargeur, AHauteur: Double);
function CalculerSurface: Double; override;
function CalculerPerimetre: Double; override;
procedure Dessiner; override;
property Largeur: Double read FLargeur write FLargeur;
property Hauteur: Double read FHauteur write FHauteur;
end;
TCercle = class(TForme)
private
FRayon: Double;
public
constructor Create(ARayon: Double);
function CalculerSurface: Double; override;
function CalculerPerimetre: Double; override;
procedure Dessiner; override;
property Rayon: Double read FRayon write FRayon;
end;
// Implémentation TRectangle
constructor TRectangle.Create(ALargeur, AHauteur: Double);
begin
inherited Create;
FLargeur := ALargeur;
FHauteur := AHauteur;
end;
function TRectangle.CalculerSurface: Double;
begin
Result := FLargeur * FHauteur;
end;
function TRectangle.CalculerPerimetre: Double;
begin
Result := 2 * (FLargeur + FHauteur);
end;
procedure TRectangle.Dessiner;
begin
ShowMessage(Format('Rectangle %s : %.2f x %.2f', [FCouleur, FLargeur, FHauteur]));
end;
// Implémentation TCercle
constructor TCercle.Create(ARayon: Double);
begin
inherited Create;
FRayon := ARayon;
end;
function TCercle.CalculerSurface: Double;
begin
Result := Pi * FRayon * FRayon;
end;
function TCercle.CalculerPerimetre: Double;
begin
Result := 2 * Pi * FRayon;
end;
procedure TCercle.Dessiner;
begin
ShowMessage(Format('Cercle %s de rayon %.2f', [FCouleur, FRayon]));
end;procedure AfficherInfosForme(Forme: TForme);
begin
Forme.Dessiner;
ShowMessage(Format('Surface : %.2f', [Forme.CalculerSurface]));
ShowMessage(Format('Périmètre : %.2f', [Forme.CalculerPerimetre]));
end;
var
Rectangle: TRectangle;
Cercle: TCercle;
begin
Rectangle := TRectangle.Create(5, 3);
try
Rectangle.Couleur := 'bleu';
AfficherInfosForme(Rectangle);
finally
Rectangle.Free;
end;
Cercle := TCercle.Create(4);
try
Cercle.Couleur := 'rouge';
AfficherInfosForme(Cercle);
finally
Cercle.Free;
end;
end;Déclare qu'une méthode peut être redéfinie dans les classes dérivées.
type
TBase = class
procedure MaMethode; virtual;
end;Redéfinit une méthode virtuelle de la classe parent.
type
TDerivee = class(TBase)
procedure MaMethode; override;
end;Masque une méthode du parent au lieu de la redéfinir (pas de polymorphisme).
type
TDerivee = class(TBase)
procedure MaMethode; reintroduce; // Cache la méthode du parent
end;Attention : reintroduce brise le polymorphisme. Préférez toujours override quand c'est possible.
Le mot-clé sealed empêche qu'une méthode soit redéfinie dans les classes dérivées :
type
TBase = class
procedure MaMethode; virtual;
end;
TDerivee = class(TBase)
procedure MaMethode; override; sealed; // Ne peut plus être redéfinie
end;
TNiveauSuivant = class(TDerivee)
// Erreur de compilation si on essaie de redéfinir MaMethode
end;Voici un exemple complet illustrant héritage et polymorphisme :
type
// Classe de base abstraite
TMethodePaiement = class
protected
FMontant: Double;
public
constructor Create(AMontant: Double);
function Traiter: Boolean; virtual; abstract;
function ObtenirDescription: string; virtual; abstract;
property Montant: Double read FMontant;
end;
// Classe dérivée : Carte de crédit
TCarteCredit = class(TMethodePaiement)
private
FNumeroCarte: string;
FNomTitulaire: string;
public
constructor Create(AMontant: Double; ANumero, ANom: string);
function Traiter: Boolean; override;
function ObtenirDescription: string; override;
end;
// Classe dérivée : PayPal
TPayPal = class(TMethodePaiement)
private
FEmail: string;
public
constructor Create(AMontant: Double; AEmail: string);
function Traiter: Boolean; override;
function ObtenirDescription: string; override;
end;
// Classe dérivée : Virement bancaire
TVirement = class(TMethodePaiement)
private
FIBAN: string;
public
constructor Create(AMontant: Double; AIBAN: string);
function Traiter: Boolean; override;
function ObtenirDescription: string; override;
end;
// Implémentation TMethodePaiement
constructor TMethodePaiement.Create(AMontant: Double);
begin
inherited Create;
FMontant := AMontant;
end;
// Implémentation TCarteCredit
constructor TCarteCredit.Create(AMontant: Double; ANumero, ANom: string);
begin
inherited Create(AMontant);
FNumeroCarte := ANumero;
FNomTitulaire := ANom;
end;
function TCarteCredit.Traiter: Boolean;
begin
// Simulation du traitement
ShowMessage('Traitement du paiement par carte de crédit...');
Result := True;
end;
function TCarteCredit.ObtenirDescription: string;
begin
Result := Format('Carte de crédit %s - %.2f €',
[FNomTitulaire, FMontant]);
end;
// Implémentation TPayPal
constructor TPayPal.Create(AMontant: Double; AEmail: string);
begin
inherited Create(AMontant);
FEmail := AEmail;
end;
function TPayPal.Traiter: Boolean;
begin
ShowMessage('Traitement du paiement PayPal...');
Result := True;
end;
function TPayPal.ObtenirDescription: string;
begin
Result := Format('PayPal (%s) - %.2f €', [FEmail, FMontant]);
end;
// Implémentation TVirement
constructor TVirement.Create(AMontant: Double; AIBAN: string);
begin
inherited Create(AMontant);
FIBAN := AIBAN;
end;
function TVirement.Traiter: Boolean;
begin
ShowMessage('Traitement du virement bancaire...');
Result := True;
end;
function TVirement.ObtenirDescription: string;
begin
Result := Format('Virement bancaire - %.2f €', [FMontant]);
end;procedure EffectuerPaiement(Paiement: TMethodePaiement);
begin
ShowMessage('Paiement : ' + Paiement.ObtenirDescription);
if Paiement.Traiter then
ShowMessage('Paiement réussi !')
else
ShowMessage('Échec du paiement');
end;
var
Carte: TCarteCredit;
PayPal: TPayPal;
Virement: TVirement;
Paiements: TList<TMethodePaiement>;
Paiement: TMethodePaiement;
begin
Paiements := TList<TMethodePaiement>.Create;
try
// Créer différents modes de paiement
Paiements.Add(TCarteCredit.Create(150.50, '1234-5678-9012-3456', 'Jean Dupont'));
Paiements.Add(TPayPal.Create(75.00, 'jean@example.com'));
Paiements.Add(TVirement.Create(200.00, 'FR76 1234 5678 9012 3456 7890 123'));
// Traiter tous les paiements (polymorphisme en action)
for Paiement in Paiements do
EffectuerPaiement(Paiement);
finally
// Libérer les objets
for Paiement in Paiements do
Paiement.Free;
Paiements.Free;
end;
end;Vérifie si un objet est d'un type spécifique :
var
Animal: TAnimal;
Chien: TChien;
begin
Chien := TChien.Create;
try
Animal := Chien; // Affectation polymorphe
if Animal is TChien then
ShowMessage('C''est un chien !');
if Animal is TChat then
ShowMessage('C''est un chat'); // Ne s'affichera pas
finally
Chien.Free;
end;
end;Convertit un objet vers un type spécifique (génère une exception si impossible) :
var
Animal: TAnimal;
Chien: TChien;
MonChien: TChien;
begin
Chien := TChien.Create;
try
Animal := Chien;
// Conversion sûre
if Animal is TChien then
begin
MonChien := Animal as TChien;
MonChien.Aboyer;
end;
finally
Chien.Free;
end;
end;-
Utilisez virtual/override pour le polymorphisme
// ✅ Bon - permet le polymorphisme procedure MaMethode; virtual; procedure MaMethode; override;
-
Déclarez abstract les méthodes sans implémentation logique dans la classe de base
function Calculer: Double; virtual; abstract;
-
Utilisez inherited pour appeler la méthode du parent
procedure MaMethode; override; begin inherited MaMethode; // Appelle le parent // Code supplémentaire end;
-
Évitez les hiérarchies trop profondes
- Maximum 3-4 niveaux idéalement
- Préférez la composition à l'héritage quand approprié
-
Utilisez des noms de classe explicites
TAnimal → TMammifere → TChien // ✅ Clair TBase → TDerivee → TSuite // ❌ Peu clair
-
Vérifiez toujours le type avant le cast
if Animal is TChien then (Animal as TChien).Aboyer;
Il est important de savoir quand utiliser l'héritage et quand utiliser la composition :
- Il y a une vraie relation "est un" (un chien est un animal)
- Vous voulez du polymorphisme
- La classe de base fournit du comportement commun
- Il y a une relation "a un" (une voiture a un moteur)
- Vous voulez plus de flexibilité
- L'héritage créerait une hiérarchie trop complexe
-
L'héritage permet à une classe de récupérer les propriétés et méthodes d'une autre classe
- Syntaxe :
class(TClasseParent) - Mot-clé
inheritedpour appeler la méthode parent
- Syntaxe :
-
Le polymorphisme permet à un objet de prendre plusieurs formes
- Utilisez
virtualdans la classe de base - Utilisez
overridedans les classes dérivées - Permet de traiter des objets différents de manière uniforme
- Utilisez
-
Méthodes abstraites (
abstract)- N'ont pas d'implémentation dans la classe de base
- Doivent être implémentées dans les classes dérivées
-
Opérateurs de type
is: vérifie le typeas: convertit vers un type (cast)
-
Avantages
- Réutilisation du code
- Organisation logique
- Flexibilité et extensibilité
- Facilite la maintenance
L'héritage et le polymorphisme sont des outils puissants qui, utilisés correctement, permettent de créer des architectures logicielles élégantes et maintenables.