Patron Composite
Fait partie de la section Programmation Orientée Objet du Journey Dart de Coddy — leçon 99 sur 110.
Le patron Composite vous permet de traiter les objets individuels et les groupes d'objets de manière uniforme. Il compose des objets en structures arborescentes où les éléments simples et les collections d'éléments partagent la même interface. C'est idéal pour représenter des hiérarchies telles que des systèmes de fichiers, des organigrammes ou des composants d'interface utilisateur.
Le patron comporte deux types de participants : les nœuds Leaf (objets individuels sans enfants) et les nœuds Composite (conteneurs qui contiennent d'autres composants). Les deux implémentent une interface Component commune :
abstract class FileSystemItem {
String get name;
int getSize();
void display([String indent = '']);
}
class File implements FileSystemItem {
@override
final String name;
final int size;
File(this.name, this.size);
@override
int getSize() => size;
@override
void display([String indent = '']) {
print('$indent- $name ($size KB)');
}
}
class Folder implements FileSystemItem {
@override
final String name;
final List<FileSystemItem> _children = [];
Folder(this.name);
void add(FileSystemItem item) => _children.add(item);
@override
int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());
@override
void display([String indent = '']) {
print('$indent+ $name/');
for (var child in _children) {
child.display('$indent ');
}
}
}
void main() {
var docs = Folder('Documents');
docs.add(File('resume.pdf', 150));
docs.add(File('photo.jpg', 2400));
var projects = Folder('Projects');
projects.add(File('main.dart', 25));
docs.add(projects);
docs.display();
print('Total: ${docs.getSize()} KB');
}La classe Folder peut contenir à la fois des objets File et d'autres objets Folder. Lorsque vous appelez getSize() sur un dossier, elle calcule de manière récursive la taille totale de tout son contenu. Le code client n'a pas besoin de savoir s'il travaille avec un seul fichier ou une hiérarchie complète de dossiers - les deux répondent aux mêmes méthodes.
Défi
FacileConstruisons un système d'organigramme en utilisant le motif Composite ! Vous allez créer une structure où les employés individuels et les départements (qui contiennent des employés et d'autres départements) peuvent être traités de manière uniforme - ce qui est parfait pour représenter les hiérarchies d'entreprise.
Vous organiserez votre code en deux fichiers :
organization.dart: Ce fichier contient votre interface de composant ainsi que les classes feuilles et composites. Créez une classe abstraiteOrganizationUnitavec un gettername, une méthodegetSalaryBudget()qui retourne unint, et une méthodedisplay([String indent = ''])pour afficher la hiérarchie. Construisez une classeEmployee(la feuille) qui implémenteOrganizationUnit. Elle doit avoir un nom et un salaire, avecgetSalaryBudget()retournant le salaire de l'employé. Sa méthodedisplay()doit afficher[indent]- [name] ($[salary]). Ensuite, créez une classeDepartment(le composite) qui implémente égalementOrganizationUnit. Elle doit avoir un nom et maintenir une liste d'enfantsOrganizationUnit. Ajoutez une méthodeadd()pour inclure des employés ou des sous-départements. Sa méthodegetSalaryBudget()doit calculer récursivement la somme des budgets de tous les enfants. Sa méthodedisplay()doit afficher[indent]+ [name]/suivi de l'affichage de chaque enfant avec une indentation augmentée (ajoutez deux espaces).main.dart: Importez votre fichier d'organisation et construisez une structure d'entreprise. Créez un départementEngineeringcontenant deux employés :Aliceavec un salaire de80000etBobavec un salaire de75000. Créez un départementQAavec une employée :Carolavec un salaire de65000. Maintenant, créez un départementTechnologyet ajoutez à la foisEngineeringetQAcomme sous-départements. Appelezdisplay()sur le département Technology, puis affichezTotal Budget: $suivi du budget salarial total.
La beauté du motif Composite est que Technology.getSalaryBudget() calcule automatiquement le total pour tous les départements et employés imbriqués - le code client n'a pas besoin de connaître la structure interne !
Sortie attendue :
+ Technology/
+ Engineering/
- Alice ($80000)
- Bob ($75000)
+ QA/
- Carol ($65000)
Total Budget: $220000Aide-mémoire
Le patron Composite vous permet de traiter les objets individuels et les groupes d'objets de manière uniforme en les composant dans des structures arborescentes où les éléments simples et les collections partagent la même interface.
Le patron se compose de :
- Composant : Une interface abstraite définissant les opérations communes
- Feuille : Des objets individuels sans enfants
- Composite : Des conteneurs qui détiennent d'autres composants (feuilles ou composites)
Exemple : Système de fichiers
abstract class FileSystemItem {
String get name;
int getSize();
void display([String indent = '']);
}
class File implements FileSystemItem {
@override
final String name;
final int size;
File(this.name, this.size);
@override
int getSize() => size;
@override
void display([String indent = '']) {
print('$indent- $name ($size KB)');
}
}
class Folder implements FileSystemItem {
@override
final String name;
final List<FileSystemItem> _children = [];
Folder(this.name);
void add(FileSystemItem item) => _children.add(item);
@override
int getSize() => _children.fold(0, (sum, item) => sum + item.getSize());
@override
void display([String indent = '']) {
print('$indent+ $name/');
for (var child in _children) {
child.display('$indent ');
}
}
}
void main() {
var docs = Folder('Documents');
docs.add(File('resume.pdf', 150));
docs.add(File('photo.jpg', 2400));
var projects = Folder('Projects');
projects.add(File('main.dart', 25));
docs.add(projects);
docs.display();
print('Total: ${docs.getSize()} KB');
}La classe Folder peut contenir à la fois des objets File et d'autres objets Folder. Lorsque vous appelez getSize() sur un dossier, il calcule de manière récursive la taille totale de tout son contenu. Le code client traite les fichiers individuels et les hiérarchies de dossiers entières de manière uniforme.
Essayez vous-même
import 'organization.dart';
void main() {
// TODO : Créer un département Ingénierie
// TODO : Ajouter Alice avec un salaire de 80000
// TODO : Ajouter Bob avec un salaire de 75000
// TODO : Créer un département QA
// TODO : Ajouter Carol avec un salaire de 65000
// TODO : Créer un département Technologie
// TODO : Ajouter Ingénierie et QA en tant que sous-départements
// TODO : Appeler display() sur le département Technologie
// TODO : Afficher "Total Budget: $" suivi du budget salarial total
}
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