L’héritage des classes Dart
L’héritage des classes Dart
Objectifs du cours
- À la fin du cours, vous serez capables de créer une classe dérivée en Dart à partir d’une classe existante en utilisant le mot-clé
extends. - Utiliser adéquatement le mot-clé
superde manière appropriée pour appeler les constructeurs des classes parentes lors de la création de classes dérivées.

-
Introduction à l’Héritage en Dart
- En programmation, l’héritage est un concept fondamental qui permet la création de nouvelles classes en se basant sur des classes existantes. En Dart, un langage de programmation développé par Google, l’héritage est pris en charge de manière similaire à d’autres langages orientés objet tels que Java ou C++.
- L’héritage est le mécanisme par lequel on créé une classe B à partir d’une autre classe existante A.
- On dit alors que la classe B hérite de la classe A. Ainsi, la classe A est appelé « classe parente » ou « super classe » et la classe B est la « classe enfant » ou « sous classe« . L’héritage en Dart se fait en utilisant le mot clé
extends. - L’héritage est la pierre angulaire de la programmation objet. En utilisant le mot clé
extends, vous pouvez permettre à une classe enfant d’hériter les propriétés et les méthodes d’une autre classe parente. De plus, la classe enfant a accès aux propriétés et méthodes de sa propre classe ainsi qu’à la classe de base. - En fait, dans le langage Dart, la classe Objet est la Super classe (parente) de toutes les classes. Par conséquent, chaque classe est dérivée d’au moins une classe ou a au moins une classe parente à l’exception de la classe Object.
-
Utilisation de l’Héritage en Dart
- En Dart, l’héritage est utilisé pour créer une relation de parenté entre les classes. La classe parente, également appelée superclasse ou classe de base, définit le comportement et les caractéristiques communes à un groupe de classes dérivées, appelées sous-classes ou classes dérivées.
- Les sous-classes héritent des membres (attributs et méthodes) de la superclasse et peuvent ajouter leurs propres membres spécifiques.
-
Définition d’une
classe de base(superclasse) : - En Dart, une classe de base, également appelée superclasse, est une classe dont d’autres classes peuvent hériter. Elle sert souvent de modèle ou de point de départ pour créer de nouvelles classes.
- Dans cet exemple, Animal est une classe de base avec deux propriétés (nom et age) et une méthode (faireDuBruit).
-
Définition d’une
sous-classequi hérite de lasuperclasse - Pour hériter d’une classe de base en Dart, on utilise le mot-clé
extends. Par exemple, voici comment créer une classe Chien qui hérite de la classe Animal : - La classe Chien hérite de la classe Animal. Elle utilise le mot-clé extends suivi du nom de la classe parente. Le constructeur de la classe fille utilise super pour appeler le constructeur de la classe parente avec les paramètres nécessaires.
-
Instanciation et utilisation de la
sous-classe - Lorsque vous exécutez le code ci-dessus, vous obtiendrez la sortie suivante :
- Ce code montre comment une classe dérivée (Chien) peut hériter des propriétés et des méthodes d’une classe de base (Animal) en Dart. L’héritage permet la réutilisation du code et la création de hiérarchies de classes.
-
Exemple
- Structure de l’exemple
- lib/models/animal.dart
- lib/models/chien.dart
- lib/services/zoo_service.dart
- lib/main.dart
-
Déclarer un constructeur d’une sous-classe (ou classe dérivée)
- En Dart, pour déclarer un constructeur d’une sous-classe (ou classe dérivée), vous utilisez le mot-clé super pour appeler le constructeur de la superclasse. Voici comment vous pouvez déclarer un constructeur pour une sous-classe :
- Supposons que nous ayons une classe de base Animal :
- Et nous avons une sous-classe Dog qui hérite de la classe Animal :
- Dans cet exemple, le constructeur de la sous-classe Dog est déclaré avec la syntaxe suivante :
- Explications :
- Dog est le nom du constructeur.
String name, int age, this.breedsont les paramètres du constructeur de la sous-classe.: super(name, age);est l’appel au constructeur de la superclasse Animal avec les arguments name et age. Cela permet d’initialiser les propriétés name et age de la superclasse.-
Applications
-
Act01: Gestion d’un Parc de Véhicules
- Enoncé
- Vous devez concevoir une application de gestion de véhicules en utilisant l’héritage de classes et l’organisation modulaire en Dart. Le programme doit compiler et s’exécuter sans erreur.
- Architecture
- Créez exactement cette structure de fichiers dans votre projet :
- Instructions pas à pas
models/vehicule.dart- Déclarez une classe Vehicule avec 3 propriétés immuables :
- Implémentez un constructeur principal.
- Ajoutez une méthode
afficherInfos()qui affiche : « [Véhicule] Marque:, Année: , Km: « models/voiture.dart- Déclarez une classe Voiture qui hérite de Vehicule.
- Ajoutez la propriété supplémentaire : nombrePortes (int).
- Implémentez le constructeur en transmettant les paramètres communs au parent.
- Surchargez
@override afficherInfos()pour appeler la version parente, puis afficher : »Nombre de portes:« - Adaptez toString() pour inclure le nombre de portes.
models/moto.dart- Déclarez une classe Moto qui hérite de Vehicule.
- Ajoutez la propriété supplémentaire : cylindree (int, en cm³).
- Implémentez le constructeur en transmettant les paramètres communs au parent.
- Surchargez @override afficherInfos() pour appeler la version parente, puis afficher : »Cylindrée:
cm³ » - Adaptez toString() en conséquence.
services/garage_service.dart- Déclarez une classe GarageService avec : nomGarage (String)
- Une liste privée _vehicules de type List
- Implémentez les méthodes suivantes :
- ajouterVehicule(Vehicule vehicule) : ajoute le véhicule à la liste et affiche un message de confirmation.
- faireControleTechnique() : parcourt tous les véhicules, appelle afficherInfos() sur chacun, et utilise l’opérateur is pour afficher un message spécifique si l’objet est une Voiture ou une Moto.
- listerVehicules() : affiche chaque véhicule en utilisant sa méthode toString().
- Un getter vehicules qui retourne une version immuable de la liste.
main.dart- Importez correctement les fichiers nécessaires.
- Dans void main() :
- Instanciez au moins 2 voitures et 2 motos avec des données différentes.
- Créez une instance de GarageService.
- Ajoutez tous les véhicules au service.
- Appelez faireControleTechnique().
- Appelez listerVehicules().
- Faites un print() direct sur un objet pour vérifier que toString() fonctionne.
-
Act02
- Objectifs du TD
- Ce TD à pour but de présenter le concept d’héritage en Dart. Il présente également un exercice sur la manipulation de tableau (via un exercice de tri).
- Rappel : Héritage
- Un cas concret : La classe Voiture représente toutes sortes de voitures possibles. On pourrait définir un camion comme une voiture très longue, très haute ? Mais un camion a des spécificités visà-vis des voitures : remorque,… On pourrait créer une classe Camion qui ressemble à la classe Voiture. Mais on ne veut pas réécrire tout ce qu’elles ont en commun.
- La solution : La classe Vehicule contient tout ce qu’il y a de commun à Camion et Voiture. Camion par exemple ne contient que ce qu’il y a de spécifique aux camions.
-
Exercice:01
- Supposons que l’on dispose de la classe Date suivante :
- On veut définir une classe DateEvenement qui associe à une date donnée un événement qui la caractérise. La solution triviale serait de définir la classe DateEvenement en redéfinissant cette classe en prenant exemple sur la classe Date et en ajoutant les nouvelles fonctionnalités que l’on juge utiles.
- Ecrire DateEvenement en utilisant l’héritage.
-
Exercice:02
- Ecrire une classe Animal qui dispose d’un attribut entier nbpattes. Cette classe dispose des méthodes suivantes :
- Le constructeur, qui prend en argument un entier (le nombre de pattes),
- String toString() qui renvoie une chaîne de caractères contenant le nombre de pattes de l’animal affiche() qui affiche le nombre de pattes de l’animal.
- Ecrire une classe Autruche qui hérite de Animal.
- Ecrire une classe Lapin qui hérite de Animal.
- Ecrire une classe Main dans laquelle la méthode main() crée un lapin et une autruche.
-
Exercice:03
- Soit le programme Dart suivant :
- a) La classe Circle hérite des variables d’instance x et y de la classe Shape. En Dart, l’héritage se fait implicitement lorsque vous étendez une classe, et les membres non privés de la classe parente sont accessibles dans la classe dérivée. Ainsi, Circle hérite des variables x et y de la classe Shape.
- b) Pour ajouter une méthode setRadius permettant de fixer la valeur du rayon et getRadius pour lire la valeur du rayon dans la classe Circle, vous pouvez effectuer les modifications suivantes :
- Dans ce code, _radius est déclaré comme une variable privée en utilisant _ au début du nom. Les méthodes setRadius et getRadius sont ajoutées pour modifier et lire la valeur du rayon respectivement.
- c) Pour ajouter une variable surface à la classe Circle et mettre à jour les méthodes setRadius et toString, vous pouvez procéder ainsi :
- Dans ce code, la variable surface est ajoutée, et les méthodes getSurface et calculateSurface sont introduites pour accéder à la surface et la calculer respectivement. La méthode setRadius est modifiée pour mettre à jour la surface après la modification du rayon.
-
Exercice:04
- Ecrivez une classe Bâtiment avec les attributs suivants:
- adresse
- La classe Bâtiment doit disposer des constructeurs suivants:
- Batiment(),
- Batiment (adresse).
- La classe Bâtiment doit contenir des accesseurs et mutateurs (ou propriétés) pour les différents attributs. La classe Bâtiment doit contenir une méthode ToString () donnant une représentation du Bâtiment.
- Ecrivez une classe Maison héritant de Bâtiment avec les attributs suivants:
- NbPieces: Le nombre de pièces de la maison.
- La classe Maison doit disposer des constructeurs suivants:
- Maison(),
- Maison(adresse, nbPieces).
- La classe Maison doit contenir des accesseurs et mutateurs (ou des propriétés) pour les différents attributs. La classe Maison doit contenir une méthode ToString () donnant une représentation de la Maison.
- Ecrivez aussi un programme afin de tester ces deux classes .
-
Exercice:05 Système de Gestion des Employés
- Une entreprise souhaite automatiser la gestion de son personnel. Tous les employés partagent des informations communes (identité, salaire, ancienneté), mais leurs métiers imposent des données et des règles de calcul différentes. Vous devez concevoir une architecture orientée objet évolutive, typée et facilement testable.
- Consignes par fichier
Dans Dart, l’héritage est implémenté via le mot-clé ‘
extends‘. Lorsqu’une classe est déclarée hériter d’une autre classe, elle devient unesous-classeet la classe dont elle hérite devient lasuperclasse.
// Déclaration de la classe Animal.
class Animal {
// Propriété pour stocker le nom de l'animal.
String nom;
// Propriété pour stocker l'âge de l'animal.
int age;
// Constructeur de la classe Animal.
// Il prend le nom et l'âge en paramètres et initialise les propriétés correspondantes.
Animal(this.nom, this.age);
// Méthode faireDuBruit.
// Affiche un message indiquant que certains animaux font du bruit.
void faireDuBruit() {
print("Certains animaux font du bruit.");
}
}
// Déclaration de la classe Chien qui étend la classe Animal.
class Chien extends Animal {
// Constructeur de la classe Chien.
// Appelle le constructeur de la classe parente (Animal) en utilisant 'super'.
Chien(String nom, int age) : super(nom, age);
// Méthode aboyer.
// Affiche un message indiquant que le chien aboie, en utilisant le nom de l'animal.
void aboyer() {
print("$nom aboie : Woof! Woof!");
}
}
void main() {
var monChien = Chien("Buddy", 3);
print("Nom du chien : ${monChien.nom}");
print("Age du chien : ${monChien.age}");
monChien.faireDuBruit(); // Appelle la méthode de la classe parente
monChien.aboyer(); // Appelle la méthode spécifique à la classe fille
}
Nom du chien : Buddy
Age du chien : 3
Certains animaux font du bruit.
Buddy aboie : Woof! Woof!
lib/
├── models/
│ ├── animal.dart // class Animal
│ └── chien.dart // class Chien extends Animal
├── services/
│ └── zoo_service.dart // class ZooService
└── main.dart // import 'models/animal.dart';
// Classe parente - représente un animal générique
class Animal {
final String nom;
final int age;
// Constructeur principal
Animal(this.nom, this.age);
// Méthode commune à tous les animaux
void sePresenter() {
print('Je suis $nom, j\'ai $age ans.');
}
// Méthode à potentiellement surcharger
void manger() {
print('$nom mange de la nourriture.');
}
// Getter utile pour les sous-classes
String get description => '$nom ($age ans)';
}
import 'animal.dart';
// Classe fille qui hérite de Animal
class Chien extends Animal {
final String race;
// ✅ Constructeur moderne avec super parameters (Dart 2.17+)
Chien(super.nom, super.age, this.race);
// 🔁 Surcharge de la méthode manger()
@override
void manger() {
super.manger(); // Appel optionnel à la version parente
print('$nom préfère les croquettes pour chien !');
}
// Méthode spécifique à Chien
void aboyer() {
print('$nom aboie : Ouaf ouaf !');
}
// Surcharge de toString() pour un debug plus clair
@override
String toString() {
return 'Chien{nom: $nom, age: $age, race: $race}';
}
}
import '../models/animal.dart';
import '../models/chien.dart';
// Service qui gère une collection d'animaux
class ZooService {
final String nomZoo;
final List<Animal> _animaux = [];
ZooService(this.nomZoo);
// Ajouter un animal au zoo
void ajouterAnimal(Animal animal) {
_animaux.add(animal);
print('✅ ${animal.nom} a été ajouté au zoo $nomZoo');
}
// Faire manger tous les animaux (polymorphisme)
void faireMangerTousLesAnimaux() {
print('\n🍽️ Heure du repas au zoo $nomZoo :');
for (final animal in _animaux) {
animal.manger(); // Appel polymorphe : version adaptée à chaque type
}
}
// Lister tous les animaux
void listerAnimaux() {
print('\n🐾 Animaux du zoo $nomZoo :');
for (final animal in _animaux) {
animal.sePresenter();
// Test de type pour accéder aux méthodes spécifiques
if (animal is Chien) {
animal.aboyer();
}
}
}
// Getter pour accéder aux animaux (en lecture seule)
List<Animal> get animaux => List.unmodifiable(_animaux);
}
import 'models/animal.dart';
import 'models/chien.dart';
import 'services/zoo_service.dart';
void main() {
print('🚀 Démarrage de l\'application Zoo\n');
// 🔹 Création d'animaux
final Animal chat = Animal('Minou', 3);
final Chien rex = Chien('Rex', 5, 'Berger Allemand');
final Chien medor = Chien('Médor', 2, 'Labrador');
// 🔹 Initialisation du service Zoo
final ZooService monZoo = ZooService('Zoo Central');
// 🔹 Ajout des animaux
monZoo.ajouterAnimal(chat);
monZoo.ajouterAnimal(rex);
monZoo.ajouterAnimal(medor);
// 🔹 Démonstration du polymorphisme
monZoo.faireMangerTousLesAnimaux();
// 🔹 Lister les animaux avec leurs spécificités
monZoo.listerAnimaux();
// 🔹 Accès direct à un chien pour méthode spécifique
print('\n🔍 Focus sur ${rex.nom} :');
rex.aboyer();
print('Description : ${rex.description}');
}
class Animal {
String name;
int age;
Animal(this.name, this.age);
void eat() {
print('$name is eating.');
}
void sleep() {
print('$name is sleeping.');
}
}
class Dog extends Animal {
String breed;
// Constructeur de la sous-classe Dog
Dog(String name, int age, this.breed) : super(name, age);
void bark() {
print('$name is barking.');
}
}
Dog(String name, int age, this.breed) : super(name, age);
lib/
├── models/
│ ├── vehicule.dart // Classe de base
│ ├── voiture.dart // Classe fille 1
│ └── moto.dart // Classe fille 2
├── services/
│ └── garage_service.dart // Classe de gestion
└── main.dart // Point d'entréemarque (String)
annee (int)
kilometrage (double)Surchargez la méthode toString() pour retourner une représentation lisible de l’objet.

class Date {
int jour, mois, annee;
// Constructeur de la classe Date
Date(int j, int m, int a) {
jour = j;
mois = m;
annee = a;
}
// Méthode pour définir une nouvelle date
void setDate(int j, int m, int a) {
jour = j;
mois = m;
annee = a;
}
// Méthode pour obtenir une représentation sous forme de chaîne de la date
@override
String toString() {
return '$jour/$mois/$annee';
}
}
Solution
// Classe DateEvenement héritant de la classe Date
class DateEvenement extends Date {
String event;
// Constructeur de la classe DateEvenement
DateEvenement(int j, int m, int a, String e) : super(j, m, a) {
event = e;
}
// Méthode pour définir une nouvelle date et un nouvel événement
void setDateEvenement(int j, int m, int a, String e) {
super.setDate(j, m, a);
event = e;
}
// Méthode pour obtenir une représentation sous forme de chaîne de la date et de l'événement
@override
String toString() {
return '${super.toString()}->$event';
}
}
Solution
class Animal {
int nbPattes;
// Constructeur de la classe Animal
Animal(this.nbPattes);
// Méthode toString pour afficher le nombre de pattes
@override
String toString() {
return 'Nombre de pattes : $nbPattes';
}
// Méthode affiche() pour afficher le nombre de pattes
void affiche() {
print('Nombre de pattes : $nbPattes');
}
}
// Classe Autruche héritant de la classe Animal
class Autruche extends Animal {
// Constructeur de la classe Autruche
Autruche(int nbPattes) : super(nbPattes);
}
// Classe Lapin héritant de la classe Animal
class Lapin extends Animal {
// Constructeur de la classe Lapin
Lapin(int nbPattes) : super(nbPattes);
}
// Classe Main
void main() {
// Création d'une autruche avec 2 pattes
Autruche autruche = Autruche(2);
// Création d'un lapin avec 4 pattes
Lapin lapin = Lapin(4);
// Affichage des informations sur l'autruche
print('Informations sur l\'autruche:');
print(autruche.toString());
autruche.affiche();
// Affichage des informations sur le lapin
print('\nInformations sur le lapin:');
print(lapin.toString());
lapin.affiche();
}
import 'dart:math';
class Shape {
double x, y;
Shape() {
x = 0;
y = 0;
}
Shape.withCoordinates(double x, double y) {
this.x = x;
this.y = y;
}
String toString() {
return "Position : ($x, $y)";
}
}
class Circle extends Shape {
static const double PI = 3.141592564;
double radius;
Circle() : radius = 0;
Circle.withCoordinatesAndRadius(double x, double y, double r) : super.withCoordinates(x, y) {
radius = r;
}
@override
String toString() {
return super.toString() + " Rayon : $radius";
}
}
void main() {
Circle c1, c2;
c1 = Circle.withCoordinatesAndRadius(1, 1, 3);
c2 = Circle();
print('${c1.toString()}\n${c2.toString()}');
}
a) De quelles variables d’instance de shape hérite la classe Circle?
b) La variable radius étant déclarée private, on ne peut la modifier de l’extérieur de la classe. Ajouter une méthode setRadius pour pouvoir le fixer et getRadius pour le lire.
c) Ajoutez une variable surface à la classe Circle. Modifiez les méthodes setRadius et toString. Ajoutez les méthodes d’accès à ce champ.
Solution
class Circle extends Shape {
static const double PI = 3.141592564;
double _radius; // La variable radius est maintenant privée et commence par _
Circle() : _radius = 0;
Circle.withCoordinatesAndRadius(double x, double y, double r) : super.withCoordinates(x, y) {
_radius = r;
}
double getRadius() {
return _radius;
}
void setRadius(double radius) {
_radius = radius;
}
@override
String toString() {
return super.toString() + " Rayon : $_radius";
}
}
class Circle extends Shape {
static const double PI = 3.141592564;
double _radius;
double _surface; // Nouvelle variable surface
Circle() : _radius = 0, _surface = 0;
Circle.withCoordinatesAndRadius(double x, double y, double r) : super.withCoordinates(x, y) {
_radius = r;
_surface = calculateSurface();
}
double getRadius() {
return _radius;
}
void setRadius(double radius) {
_radius = radius;
_surface = calculateSurface(); // Mettre à jour la surface après modification du rayon
}
double getSurface() {
return _surface;
}
double calculateSurface() {
return PI * _radius * _radius;
}
@override
String toString() {
return super.toString() + " Rayon : $_radius, Surface : $_surface";
}
}
Solution
class Batiment {
String adresse;
// Constructeur par défaut
Batiment() {
adresse = "Adresse par défaut";
}
// Constructeur avec adresse
Batiment.withAdresse(this.adresse);
// Accesseurs et mutateurs
String getAdresse() {
return adresse;
}
void setAdresse(String nouvelleAdresse) {
adresse = nouvelleAdresse;
}
// Méthode ToString
@override
String toString() {
return 'Batiment{adresse: $adresse}';
}
}
class Maison extends Batiment {
int nbPieces;
// Constructeur par défaut
Maison() : super() {
nbPieces = 0;
}
// Constructeur avec adresse et nombre de pièces
Maison.withAdresseNbPieces(String adresse, this.nbPieces) : super.withAdresse(adresse);
// Accesseurs et mutateurs pour nbPieces
int getNbPieces() {
return nbPieces;
}
void setNbPieces(int nouveauNbPieces) {
nbPieces = nouveauNbPieces;
}
// Méthode ToString
@override
String toString() {
return 'Maison{adresse: $adresse, nbPieces: $nbPieces}';
}
}
void main() {
// Test des classes Batiment et Maison
Batiment batiment1 = Batiment();
print(batiment1);
Batiment batiment2 = Batiment.withAdresse("123 Rue de la Test");
print(batiment2);
Maison maison1 = Maison();
print(maison1);
Maison maison2 = Maison.withAdresseNbPieces("456 Avenue du Test", 5);
print(maison2);
// Test des accesseurs et mutateurs
batiment1.setAdresse("789 Boulevard de l'Exemple");
print(batiment1.getAdresse());
maison1.setNbPieces(3);
print(maison1.getNbPieces());
}
lib/
├── models/
│ ├── employe.dart
│ ├── developpeur.dart
│ ├── designer.dart
│ └── manager.dart
├── services/
│ └── rh_service.dart
└── main.dart1️⃣ models/employe.dart
-
Classe de base Employe avec les propriétés immuables :
nom (String), prenom (String)
salaire (double), anciennete (int en années)
Constructeur principal
Méthode void afficherFiche() affichant :
« [Employé]
Méthode double calculerBonus() retournant un bonus de base (ex: salaire * 0.05)
@override String toString() retournant une représentation compacte de l’objet
2️⃣ models/developpeur.dart
-
Hérite de Employe
Propriétés supplémentaires : langages (List
Constructeur avec super parameters
@override afficherFiche() : appelle super, puis affiche « Langages: […] » et « Niveau: … »
@override calculerBonus() :
Senior → salaire * 0.12
Mid → salaire * 0.08
Junior → salaire * 0.05
3️⃣ models/designer.dart
-
Hérite de Employe
Propriétés supplémentaires : outils (List
Constructeur avec super
@override afficherFiche() : ajoute « Outils: […] » et « Spécialité: … »
@override calculerBonus() : retourne salaire * 0.07 + 200 (prime forfaitaire)
4️⃣ models/manager.dart
-
Hérite de Employe
Propriétés supplémentaires : tailleEquipe (int), budgetDepartement (double)
Constructeur avec super
@override afficherFiche() : ajoute « Équipe: … personnes » et « Budget: …€ »
@override calculerBonus() : retourne salaire * 0.15 + (tailleEquipe * 50)
5️⃣ services/rh_service.dart
-
Classe RhService avec :
nomEntreprise (String)
_employes (List
Méthodes :
ajouterEmploye(Employe e) : ajout + message de confirmation
listerEmployes() : affiche chaque employé via toString()
distribuerBonus() : parcourt la liste, appelle afficherFiche() et calculerBonus(), affiche « Bonus versé :
trierParSalaireCroissant() : trie la liste interne
genererStatistiques() : utilise is pour compter le nombre de développeurs, designers et managers, et affiche un récapitulatif
Getter employes retournant List.unmodifiable(_employes)
6️⃣ main.dart
-
Importez correctement les fichiers models/ et services/
Dans void main() :
Instanciez au moins 2 employés de chaque type avec des données variées et cohérentes
Créez une instance de RhService
Ajoutez tous les employés au service
Appelez listerEmployes()
Appelez distribuerBonus()
Triez la liste par salaire croissant, puis réaffichez l’inventaire
Appelez genererStatistiques()
Testez explicitement toString() avec un print() direct sur un objet
