Bases du polymorphisme
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 57 sur 110.
Le polymorphisme signifie « plusieurs formes » : c'est la capacité de traiter des objets de différentes classes via une interface commune. Lorsque vous avez une référence de classe parente, elle peut pointer vers n'importe quel objet de classe enfant, et la méthode appropriée est appelée en fonction du type d'objet réel.
C'est puissant car vous pouvez écrire du code qui fonctionne avec le type parent, tout en gérant automatiquement tous les types enfants correctement :
class Animal {
void speak() => print('Some sound');
}
class Dog extends Animal {
@override
void speak() => print('Woof!');
}
class Cat extends Animal {
@override
void speak() => print('Meow!');
}
void main() {
Animal myPet = Dog(); // Type parent, objet enfant
myPet.speak(); // Sortie : Woof!
myPet = Cat(); // Même variable, objet différent
myPet.speak(); // Sortie : Meow!
}La variable myPet est déclarée comme Animal, mais elle contient un Dog ou un Cat. Lorsque speak() est appelée, Dart examine le type réel de l'objet au moment de l'exécution et appelle la méthode redéfinie appropriée.
Cela vous permet d'écrire des fonctions flexibles qui fonctionnent avec n'importe quelle sous-classe :
void makeAllSpeak(List<Animal> animals) {
for (var animal in animals) {
animal.speak(); // Chaque animal s'exprime à sa manière
}
}
void main() {
var pets = [Dog(), Cat(), Dog()];
makeAllSpeak(pets);
// Sortie : Woof! Meow! Woof!
}La fonction n'a pas besoin de connaître Dog ou Cat - elle fonctionne simplement avec Animal. Cela rend votre code plus extensible car l'ajout de nouveaux types d'animaux ne nécessite aucune modification des fonctions existantes.
Défi
FacileConstruisons un système d'instruments de musique qui démontre le polymorphisme en action. Vous allez créer une classe de base Instrument et plusieurs types d'instruments, puis écrire une fonction capable de faire jouer ensemble n'importe quelle collection d'instruments - sans connaître leurs types spécifiques.
Vous organiserez votre code en deux fichiers :
instruments.dart: Définissez votre hiérarchie d'instruments ici :- Une classe
Instrumentavec une propriétéString nameet un constructeur. Incluez une méthodeplay()qui affiche'$name makes a sound' - Une classe
Guitarqui étendInstrumentet surchargeplay()pour afficher'$name strums melodically' - Une classe
Drumqui étendInstrumentet surchargeplay()pour afficher'$name beats rhythmically' - Une classe
Pianoqui étendInstrumentet surchargeplay()pour afficher'$name plays harmoniously'
performConcert(List<Instrument> instruments)qui affiche'Concert begins!', puis parcourt la liste en appelantplay()sur chaque instrument, et affiche enfin'Concert ends!'- Une classe
main.dart: Importez votre fichier d'instruments et démontrez le polymorphisme en créant un groupe mixte :- Créez une liste de type
List<Instrument>contenant uneGuitarnommée'Acoustic', unDrumnommé'Snare', et unPianonommé'Grand' - Passez cette liste à
performConcert()
- Créez une liste de type
Remarquez comment performConcert() fonctionne avec le type parent Instrument, pourtant chaque instrument joue de sa propre manière unique. C'est le polymorphisme - le même appel de méthode produit un comportement différent selon le type réel de l'objet.
Sortie attendue :
Concert begins!
Acoustic strums melodically
Snare beats rhythmically
Grand plays harmoniously
Concert ends!Aide-mémoire
Le polymorphisme signifie "plusieurs formes" - il vous permet de traiter des objets de différentes classes via une interface commune. Une référence de classe parente peut pointer vers n'importe quel objet de classe enfant, et la méthode correcte est appelée en fonction du type d'objet réel au moment de l'exécution.
Exemple de polymorphisme de base :
class Animal {
void speak() => print('Some sound');
}
class Dog extends Animal {
@override
void speak() => print('Woof!');
}
class Cat extends Animal {
@override
void speak() => print('Meow!');
}
void main() {
Animal myPet = Dog(); // Type parent, objet enfant
myPet.speak(); // Output: Woof!
myPet = Cat(); // Même variable, objet différent
myPet.speak(); // Output: Meow!
}Le polymorphisme permet de créer des fonctions flexibles qui fonctionnent avec n'importe quelle sous-classe :
void makeAllSpeak(List<Animal> animals) {
for (var animal in animals) {
animal.speak(); // Chaque animal s'exprime à sa manière
}
}
void main() {
var pets = [Dog(), Cat(), Dog()];
makeAllSpeak(pets);
// Output: Woof! Meow! Woof!
}La fonction travaille avec le type parent Animal sans avoir besoin de connaître les classes enfants spécifiques comme Dog ou Cat. Cela rend le code plus extensible - l'ajout de nouveaux types ne nécessite aucune modification des fonctions existantes.
Essayez vous-même
import 'instruments.dart';
void main() {
// TODO: Créer une List<Instrument> contenant :
// - Une Guitar nommée 'Acoustic'
// - Un Drum nommé 'Snare'
// - Un Piano nommé 'Grand'
// TODO: Appeler performConcert() avec votre liste d'instruments
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Les bases de la POO
Fichiers externesBibliothèques et importsIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesVariables d'instanceBases des constructeursRécapitulatif - Calculatrice simple4Null Safety
Introduction à la Null SafetyNullable vs Non-NullableLes opérateurs ? et !Le mot-clé late et la Null SafetyLes opérateurs Null-AwareLa Null Safety dans les classesRécapitulatif - Système de profil utilisateur7Classes abstraites et Interfaces
Classes abstraitesMéthodes abstraitesInterfaces en DartInterfaces implicitesImplémentation vs ExtensionInterfaces multiplesRécapitulatif - Calculateur de formes10Collections et Génériques
Aperçu des List, Set et MapCollections Type-SafeClasses génériquesMéthodes génériquesContraintes de généricitéIterable & IteratorRécapitulatif - Stockage générique2Les constructeurs en Dart
Constructeur par défautConstructeurs nommésListes d'initialisationConstructeurs constantsConstructeurs factoryConstructeurs de redirectionRécapitulatif - Shape Builder5Encapsulation
Membres publics vs privésLa convention du préfixe _Visibilité au niveau de la bibliothèqueApprofondissement des Getters & SettersMasquage d'informationsRécapitulatif - Dossiers d'étudiants8Mixins
Introduction aux MixinsCréation de MixinsUtilisation de plusieurs MixinsLe mot-clé on dans les MixinsMixin vs HéritageMixin vs InterfaceRécapitulatif - Système Animal11Méthodes spéciales
Redéfinition de toString()Redéfinition de hashCode & ==Interface ComparableMéthode call()Redéfinition de noSuchMethodRécapitulatif - Collection personnalisée14Patrons de conception Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron ObservateurPatron Stratégie3Propriétés de classe
Membres d'instance vs statiquesChamps Final & ConstVariables LateMéthodes et champs statiquesGetters et SettersRécapitulatif - Gestionnaire de compte bancaire6Héritage
Bases de l'héritageLe mot-clé superRedéfinition de méthodeL'annotation @overrideLe mot-clé final pour les classesConstructeurs et héritageRécapitulatif - Hiérarchie des employés9Polymorphisme
Bases du polymorphismePolymorphisme via les interfacesVérification de type à l'exécutionLes opérateurs is & asMot-clé covariantRécapitulatif - Processeur de paiement12POO Asynchrone
Futures & async/awaitBases des StreamsStream ControllersConstructeurs asynchronesL'asynchrone dans les méthodes de classeRécapitulatif - Data Fetcher15Patrons de conception Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode ModèlePatron ÉtatPatron CompositePatron Repository