Composition ou héritage
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy. Leçon 82 sur 110.
Vous avez appris que l'héritage crée une relation « est-un » — un Dog est un Animal. Mais il existe une autre approche puissante : la composition, qui crée une relation « a-un ». Au lieu d'hériter d'un comportement, une classe contient des instances d'autres classes et leur délègue le travail.
Considérez une classe Car. Avec l'héritage, vous pourriez essayer d'étendre une classe Engine - mais une voiture n'est pas un moteur, elle a un moteur :
class Engine {
void start() => print('Engine started');
}
class Car {
final Engine _engine = Engine(); // Composition : Car A un Engine
void start() {
_engine.start(); // Déléguer au moteur
print('Car is ready to drive');
}
}
void main() {
var car = Car();
car.start();
}L'avantage clé de la composition est la flexibilité. Avec l'héritage, vous êtes lié à une seule classe parente. Avec la composition, vous pouvez combiner plusieurs composants et même les échanger au moment de l'exécution :
class ElectricEngine {
void start() => print('Electric motor humming');
}
class HybridCar {
Engine? _gasEngine;
ElectricEngine? _electricEngine;
HybridCar({bool useElectric = false}) {
if (useElectric) {
_electricEngine = ElectricEngine();
} else {
_gasEngine = Engine();
}
}
}Une directive courante est « favoriser la composition plutôt que l'héritage ». Utilisez l'héritage lorsqu'il existe une véritable relation « est un » (is-a) et que vous avez besoin de polymorphisme. Utilisez la composition lorsque vous souhaitez réutiliser un comportement sans couplage étroit, ou lorsqu'un objet a besoin de capacités provenant de plusieurs sources.
Défi
FacileConstruisons un système informatique en utilisant la composition ! Au lieu d’essayer de faire hériter un ordinateur de ses composants, tu vas créer des classes de composants séparées et les composer ensemble : comme un ordinateur possède un CPU et possède de la mémoire, ce n’est ni un CPU ni de la mémoire.
Tu organiseras ton code en trois fichiers :
components.dart: crée deux classes de composants qui représentent les pièces de l’ordinateur :- Une classe
CPUavec unString brandet undouble speedGHz. Inclus une méthodeprocess()qui renvoie une chaîne de caractères :[brand] processing at [speedGHz]GHz - Une classe
Memoryavec unint sizeGB. Inclus une méthodeload()qui renvoie une chaîne de caractères :Loading [sizeGB]GB of data
- Une classe
computer.dart: crée une classeComputerqui utilise la composition pour combiner les composants. Ton ordinateur doit :- Posséder un champ
String name - Contenir une instance de
CPUet une instance deMemory(composition : l’ordinateur POSSÈDE ces composants) - Accepter toutes les valeurs nécessaires via son constructeur afin de créer les deux composants en interne
- Posséder une méthode
boot()qui délègue le travail aux deux composants et affiche trois lignes : le nom de l’ordinateur suivi destarting..., puis le résultat de la méthode process du CPU, puis le résultat de la méthode load de la Memory - Posséder une méthode
specs()qui renvoie une chaîne décrivant le système :[name]: [brand] [speedGHz]GHz, [sizeGB]GB RAM
- Posséder un champ
main.dart: importe tes fichiers et montre la composition en action :- Crée un
ComputernomméWorkstationavec un CPUIntelfonctionnant à3.5GHz et16Go de mémoire - Appelle la méthode
boot() - Affiche le résultat de
specs()
- Crée un
Remarque que la classe Computer n’étend ni CPU ni Memory : elle les contient et leur délègue le travail. C’est la relation « possède-un » que crée la composition !
Résultat attendu :
Workstation starting...
Intel processing at 3.5GHz
Loading 16GB of data
Workstation: Intel 3.5GHz, 16GB RAMEssayez vous-même
import 'computer.dart';
void main() {
// TODO: Créer un Computer nommé "Workstation" avec :
// - CPU Intel cadencé à 3.5 GHz
// - 16 GB de mémoire
// TODO: Appeler la méthode boot()
// TODO: Afficher le résultat de specs()
}
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
1Fondamentaux de la POO
Fichiers externesBibliothèques et importsIntroduction à la POOClasses vs objetsLe mot-clé thisMéthodesVariables d’instanceBases des constructeursRécapitulatif - Calculatrice simple4Sécurité face aux valeurs nulles
Introduction à la sécurité face aux valeurs nullesNullable vs Non-NullableLes opérateurs ? et !Mot-clé Late et sécurité face aux valeurs nullesOpérateurs sensibles à nullSécurité face aux valeurs nulles dans les classesRécapitulatif – Système de profil utilisateur7Classes abstraites et interfaces
Classes abstraitesMéthodes abstraitesInterfaces en DartInterfaces implicitesImplémenter ou étendreInterfaces multiplesRécapitulatif - Calculateur de formes10Collections et génériques
Vue d’ensemble des List, Set et MapCollections à typage sûrClasses génériquesMéthodes génériquesContraintes génériquesIterable et IteratorRécapitulatif – stockage générique13Concepts avancés de la POO
Composition ou héritageMéthodes d’extensionClasses appelablesClasses scellées (Dart 3)Enregistrements (Dart 3)Patterns et correspondance (3.0)Énumérations avec méthodes2Constructeurs en Dart
Constructeur par défautConstructeurs nommésListes d'initialisationConstructeurs constantsConstructeurs factoryConstructeurs redirigésRécapitulatif - Constructeur de formes5Encapsulation
Membres publics vs privésLa convention du préfixe _Confidentialité au niveau de la bibliothèqueApprofondissement des getters et settersMasquage de l’informationRécapitulatif - Fiches des étudiants8Mixins
Introduction aux MixinsCréer des MixinsUtiliser plusieurs MixinsMot-clé on dans les MixinsMixin ou héritageMixin ou interfaceRécapitulatif - Système animal11Méthodes spéciales
Redéfinition de toString()Redéfinition de hashCode et ==Interface ComparableMéthode call()Redéfinition de noSuchMethodRécapitulatif - Collection personnalisée14Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy3Propriétés des classes
Membres d’instance et statiquesChamps Final et ConstVariables différéesMéthodes et champs statiquesAccesseurs et mutateursRécapitulatif - Gestionnaire de compte bancaire6Héritage
Héritage de baseLe mot-clé superRedéfinition de méthodeL’annotation @overrideLe mot-clé de classe finalConstructeurs et héritageRécapitulatif – Hiérarchie des employés9Polymorphisme
Bases du polymorphismePolymorphisme via les interfacesVérification des types à l’exécutionLes opérateurs is et asMot-clé covariantRécapitulatif - Processeur de paiements12POO asynchrone
Futures et async/awaitNotions de base des StreamsContrôleurs de StreamsConstructeurs asynchronesAsynchrone dans les méthodes de classeRécapitulatif - récupérateur de données15Patrons de conception – Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode modèlePatron ÉtatPatron CompositePatron DépôtEntraînez-vous par vous-même : Compilateur Dart en ligne