Menu
Coddy logo textTech

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.

challenge icon

Défi

Facile

Construisons 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 abstraite OrganizationUnit avec un getter name, une méthode getSalaryBudget() qui retourne un int, et une méthode display([String indent = '']) pour afficher la hiérarchie. Construisez une classe Employee (la feuille) qui implémente OrganizationUnit. Elle doit avoir un nom et un salaire, avec getSalaryBudget() retournant le salaire de l'employé. Sa méthode display() doit afficher [indent]- [name] ($[salary]). Ensuite, créez une classe Department (le composite) qui implémente également OrganizationUnit. Elle doit avoir un nom et maintenir une liste d'enfants OrganizationUnit. Ajoutez une méthode add() pour inclure des employés ou des sous-départements. Sa méthode getSalaryBudget() doit calculer récursivement la somme des budgets de tous les enfants. Sa méthode display() 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épartement Engineering contenant deux employés : Alice avec un salaire de 80000 et Bob avec un salaire de 75000. Créez un département QA avec une employée : Carol avec un salaire de 65000. Maintenant, créez un département Technology et ajoutez à la fois Engineering et QA comme sous-départements. Appelez display() sur le département Technology, puis affichez Total 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: $220000

Aide-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
}
quiz iconTestez-vous

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