Modèle de conception Composite en Java

Définition

Le modèle Composite permet de structurer des objets sous forme d’arborescence pour représenter une relation « partie-tout ». Il offre une interface uniforme pour manipuler aussi bien des éléments isolés (feuilles) que des groupes d’éléments (composés), ce qui simplifie l’interaction avec des hiérarchies complexes — par exemple, la gestion des fichiers et dossiers dans un système de fichiers.

Architecture du modèle

  • Component : interface ou classe abstraite définissant les opérations communes (ex. operation()) et servant de base à tous les éléments.
  • Leaf : implémentation concrète représentant un élément terminal, sans sous-éléments.
  • Composite : implémentation concrète contenant une collection d’autres Component, capable de déléguer les opérations à ses enfants.

Implémentation sécurisée (safe composition)

Contrairement à l’approche « transparente » où toutes les méthodes de gestion hiérarchique sont déclarées dans Component (obligeant les feuilles à les implémenter artificiellement), l’approche sécurisée limite ces méthodes aux seuls objets composites. Cela évite les exceptions inattendues et renforce la cohérence du contrat d’interface.

Classe abstraite Component (version sécurisée) :

public abstract class Component {
    protected final String label;

    protected Component(String label) {
        this.label = label;
    }

    public abstract void execute();
}

Classe Leaf (inchangée) :

public class Leaf extends Component {
    public Leaf(String label) {
        super(label);
    }

    @Override
    public void execute() {
        System.out.println("→ " + label + " (feuille)");
    }
}

Classe Composite (avec gestion interne des enfants) :

import java.util.*;

public class Composite extends Component {
    private final List<Component> items = new ArrayList<>();

    public Composite(String label) {
        super(label);
    }

    public void add(Component item) {
        items.add(item);
    }

    public boolean remove(Component item) {
        return items.remove(item);
    }

    public Optional<Component> get(int index) {
        return (index >= 0 && index < items.size())
                ? Optional.of(items.get(index))
                : Optional.empty();
    }

    @Override
    public void execute() {
        System.out.println("▶ " + label + " (groupe)");
        items.forEach(Component::execute);
    }
}

Client utilisant la hiérarchie :

public class Application {
    public static void main(String[] args) {
        Composite root = new Composite("Disque C:");
        Composite folderA = new Composite("Projet");
        Composite folderB = new Composite("Ressources");
        Composite subfolder = new Composite("Sources");
        
        Component fileX = new Leaf("pom.xml");
        Component fileY = new Leaf("README.md");

        subfolder.add(fileX);
        folderA.add(subfolder);
        folderB.add(fileY);

        root.add(folderA);
        root.add(folderB);

        root.execute(); // Exécution récursive complète
        System.out.println("—".repeat(25));

        root.remove(folderA);
        root.execute();
        System.out.println("—".repeat(25));

        root.get(0).ifPresent(Component::execute); // Exécute Ressources
    }
}

Utilisation dans Spring Framework

Spring adopte couramment le modèle Composite de façon sécurisée. Par exemple :

  • CompositeCacheManager agrège plusieurs implémentations de CacheManager sous une même interface.
  • CompositeIterator fusionne plusieurs itérateurs en un seul flux énumérable.

Ces cas illustrent comment Spring exploite la copmosition pour offrir une abstraction souple tout en préservant la sécurité typique au niveau de l’API.

Avantages et limites

Avantages :

  • Interface homogène pour les clients, indépendante de la profondeur ou du type d’élément.
  • Extension facile via l’ajout de nouvelles classes Leaf ou Composite, sans modification du code existant (principe OCP).
  • Hiérarchies dynamiques et modulaires, faciles à construire, parcourir ou modifier.

Limites :

  • Aucune vérification statique du type des éléments contenus dans un Composite — impossible d’imposer, par exemple, qu’un dossier n’accepte que des fichiers texte.
  • La séparation stricte des interfaces peut nécessiter des casts explicites ou des vérifications instanceof si le client doit traiter différemment les feuilles et les composites.

Principe fondamental

Le modèle Composite repose sur l’abstraction commune : il masque la différence entre éléments simples et agrégats, permettant ainsi de concevoir des algorithmes génériques qui opèrent récursivement sur toute la structure arborescente — sans connaître sa forme exacte à l’avance.

Étiquettes: composite-pattern java-design-patterns spring-framework Object-Oriented-Design

Publié le 20 octobre à 05h17