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 :
CompositeCacheManageragrège plusieurs implémentations deCacheManagersous une même interface.CompositeIteratorfusionne 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
LeafouComposite, 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
instanceofsi 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.