Implémentation vs Extension
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 47 sur 110.
Maintenant que vous comprenez à la fois extends et implements, clarifions quand utiliser chacun d'eux.
Utilisez extends lorsque vous souhaitez hériter d'un comportement. Votre sous-classe obtient gratuitement tout le code du parent et peut s'appuyer dessus :
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Hérité - fonctionne automatiquement
dog.bark();
}Utilisez implements lorsque vous voulez garantir une structure sans hériter de code. Vous devez écrire chaque méthode vous-même :
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...'); // Implémentation complètement personnalisée
}
}La différence clé : extends dit « Je suis un type de ceci » tandis que implements dit « Je peux faire ce que ceci fait. » Un Dog est un Animal et respire de la même manière. Un Robot n'est pas un animal, mais il peut remplir le même contrat avec sa propre logique.
Rappelez-vous : vous ne pouvez étendre qu'une seule classe, mais vous pouvez implémenter plusieurs interfaces. Choisissez extends pour les véritables relations d'héritage, et implements lorsque vous avez besoin de flexibilité ou que vous souhaitez imposer un contrat sans partager les détails d'implémentation.
Défi
FacileConstruisons un système de travailleurs qui démontre quand utiliser extends par rapport à implements. Vous allez créer une classe de base pour les travailleurs, puis montrer les deux approches : une classe qui "est" véritablement un travailleur et hérite de son comportement, et une autre qui peut simplement "faire" ce qu'un travailleur fait, mais avec une logique complètement personnalisée.
Vous organiserez votre code dans deux fichiers :
worker.dart: Définissez votre hiérarchie de travailleurs ici :- Une classe
Workeravec une propriétéString nameet un constructeur. Incluez une méthodework()qui affiche[name] is working...et une méthodetakeBreak()qui affiche[name] takes a coffee break - Une classe
OfficeWorkerqui étend (extends)Worker. Ajoutez une propriétéString department. Surchargezwork()pour afficher[name] is typing reports in [department]mais conservez le comportement hérité detakeBreak() - Une classe
RobotWorkerqui implémente (implements)Worker. Ajoutez une propriétéString model. Comme les robots ne sont pas vraiment des travailleurs mais peuvent faire ce que les travailleurs font, fournissez des implémentations complètement personnalisées :work()doit afficherRobot [model] is assembling partsettakeBreak()doit afficherRobot [model] is recharging
- Une classe
main.dart: Importez votre fichier de travailleur et démontrez les deux approches :- Créez un
OfficeWorkernommé'Alice'dans le département'Marketing' - Créez un
RobotWorkeravec le modèle'RX-7' - Pour l'employé de bureau, appelez
work()puistakeBreak() - Affichez une ligne vide
- Pour le robot travailleur, appelez
work()puistakeBreak()
- Créez un
Remarquez comment OfficeWorker hérite automatiquement de takeBreak() parce qu'il étend Worker - il "est" véritablement un travailleur. En revanche, RobotWorker doit fournir sa propre implémentation pour tout car il n'implémente que l'interface - il "peut faire" ce qu'un travailleur fait, mais à sa propre manière robotique.
Sortie attendue :
Alice is typing reports in Marketing
Alice takes a coffee break
Robot RX-7 is assembling parts
Robot RX-7 is rechargingAide-mémoire
Utilisez extends pour hériter du comportement d'une classe parente. La sous-classe reçoit automatiquement toutes les méthodes parentes et peut les surcharger :
class Animal {
void breathe() {
print('Breathing...');
}
}
class Dog extends Animal {
void bark() {
print('Woof!');
}
}
void main() {
var dog = Dog();
dog.breathe(); // Inherited automatically
dog.bark();
}Utilisez implements pour garantir une structure sans hériter du code. Vous devez fournir votre propre implémentation pour chaque méthode :
class Robot implements Animal {
@override
void breathe() {
print('Ventilating circuits...');
}
}Différences clés :
extends: « Je suis un type de ceci » - véritable héritage avec partage de codeimplements: « Je peux faire ce que ceci fait » - impose un contrat avec une logique personnalisée- Vous ne pouvez étendre qu'une seule classe, mais vous pouvez implémenter plusieurs interfaces
Choisissez extends pour les véritables relations parent-enfant où le comportement doit être hérité. Choisissez implements lorsque vous avez besoin de flexibilité ou que vous souhaitez imposer un contrat sans partager l'implémentation.
Essayez vous-même
import 'worker.dart';
void main() {
// TODO: Créer un OfficeWorker nommé 'Alice' dans le département 'Marketing'
// TODO: Créer un RobotWorker avec le modèle 'RX-7'
// TODO: Pour l'employé de bureau, appeler work() puis takeBreak()
// TODO: Afficher une ligne vide
// TODO: Pour le robot, appeler work() puis takeBreak()
}
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