Menu
Coddy logo textTech

Patron Composite

Fait partie de la section Programmation Orientée Objet du Journey PHP de Coddy — leçon 80 sur 91.

Le Patron Composite est un patron de conception structurel qui vous permet de composer des objets dans des structures arborescentes, puis de travailler avec ces structures comme s'il s'agissait d'objets individuels. Il est parfait pour représenter des hiérarchies partie-tout où vous souhaitez traiter les objets individuels et les groupes d'objets de manière uniforme.

Considérez un système de fichiers. Un dossier peut contenir des fichiers et d'autres dossiers.

Lorsque vous calculez la taille totale, vous voulez appeler la même méthode sur les fichiers et les dossiers, et les dossiers doivent additionner leur contenu de manière récursive. Le patron Composite rend cela transparent en donnant aux feuilles (fichiers) et aux composites (dossiers) la même interface.

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

Maintenant, vous pouvez construire des structures arborescentes et les traiter de manière uniforme :

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize();

Résultat :

1810

Le code client appelle getSize() sans se soucier de savoir s'il traite un fichier unique ou une structure de dossiers profondément imbriquée. Le patron gère la récursion en interne, rendant les hiérarchies complexes faciles à manipuler.

challenge icon

Défi

Facile

Construisons un système d'organigramme en utilisant le Design Pattern Composite. Les entreprises ont une structure hiérarchique où les départements peuvent contenir des employés et d'autres sous-départements — une structure arborescente parfaite où vous souhaitez calculer des totaux de manière uniforme dans toute l'organisation.

Vous organiserez votre code sur quatre fichiers :

  • OrganizationUnit.php — Définissez une interface OrganizationUnit que les employés individuels et les départements implémenteront. Elle doit avoir deux méthodes : getName(): string et getSalary(): int. Cette interface commune vous permet de traiter les employés individuels et les départements entiers de la même manière lors du calcul des coûts.
  • Employee.php — Incluez l'interface OrganizationUnit et créez une classe Employee qui l'implémente. Un employé est un nœud feuille dans votre arbre d'organisation — il a un nom et un salaire mais ne peut pas contenir d'autres unités. Utilisez la promotion de constructeur pour accepter le nom et le salaire, et implémentez les deux méthodes de l'interface.
  • Department.php — Incluez l'interface OrganizationUnit et créez une classe Department qui l'implémente. Un département est un composite qui peut contenir à la fois des employés et d'autres départements. Votre département doit :
    • Accepter un nom dans son constructeur
    • Avoir une méthode add(OrganizationUnit $unit): void pour ajouter des employés ou des sous-départements
    • Implémenter getSalary() pour retourner le salaire total de toutes les unités qu'il contient (en sommant récursivement tout ce qui se trouve à l'intérieur)
  • main.php — Incluez les fichiers Employee et Department. Vous recevrez une entrée : une chaîne JSON représentant la structure de l'organisation.

    Le JSON sera dans ce format :

    {"Engineering": [{"name": "Alice", "salary": 80000}, {"name": "Bob", "salary": 75000}], "Sales": [{"name": "Carol", "salary": 65000}]}

    Chaque clé est un nom de département, et sa valeur est un tableau d'employés avec leurs noms et salaires.

    Créez un Department racine appelé "Company". Pour chaque département dans le JSON, créez un Department, ajoutez ses employés, puis ajoutez ce département à la Company. Enfin, affichez le salaire total de l'entreprise dans ce format :

    Total Salary: [amount]

La beauté du Design Pattern Composite est que l'appel à getSalary() sur le département Company calcule automatiquement la somme de tous les départements et employés imbriqués — le code client n'a pas besoin de connaître la structure interne.

Aide-mémoire

Le Composite Pattern (ou patron de conception Composite) est un patron de conception structurel qui permet de composer des objets dans des structures en arbre et de traiter les objets individuels et les groupes de manière uniforme.

Il est idéal pour les hiérarchies partie-tout où vous souhaitez la même interface pour les nœuds feuilles et les nœuds composites.

Composants clés

  • Interface Composant — Définit les opérations communes aux feuilles et aux composites
  • Feuille (Leaf) — Représente les objets individuels qui ne peuvent pas contenir d'autres objets
  • Composite — Contient des enfants (feuilles ou autres composites) et implémente les opérations en les déléguant aux enfants

Exemple : Système de fichiers

<?php
interface FileSystemItem {
    public function getName(): string;
    public function getSize(): int;
}

class File implements FileSystemItem {
    public function __construct(
        private string $name,
        private int $size
    ) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function getSize(): int {
        return $this->size;
    }
}

class Folder implements FileSystemItem {
    private array $items = [];
    
    public function __construct(private string $name) {}
    
    public function getName(): string {
        return $this->name;
    }
    
    public function add(FileSystemItem $item): void {
        $this->items[] = $item;
    }
    
    public function getSize(): int {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getSize();
        }
        return $total;
    }
}

Utilisation

<?php
$docs = new Folder("Documents");
$docs->add(new File("resume.pdf", 250));
$docs->add(new File("cover.docx", 50));

$images = new Folder("Images");
$images->add(new File("photo.jpg", 1500));

$root = new Folder("Root");
$root->add($docs);
$root->add($images);
$root->add(new File("readme.txt", 10));

echo $root->getSize(); // 1810

Le client appelle getSize() de manière uniforme sur les fichiers et les dossiers. Le composite gère la récursion en interne, ce qui rend les hiérarchies complexes simples à manipuler.

Essayez vous-même

<?php
require_once 'Employee.php';
require_once 'Department.php';

// Lire l'entrée JSON
$input = trim(fgets(STDIN));
$data = (array)json_decode($input, true);

// TODO: Créer un département racine appelé "Company"

// TODO: Parcourir chaque département dans les données JSON
// Pour chaque département :
//   - Créer un nouvel objet Department
//   - Ajouter chaque employé à ce département
//   - Ajouter le département à Company

// TODO: Afficher le salaire total au format : Total Salary: [amount]

?>
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