Les struc­tures de données dy­na­miques telles que l’ar­bo­res­cence d’une gestion de fichiers ou d’une interface de programme né­ces­si­tent une structure hié­rar­chique claire et sans ambiguïté. Cependant, la mise en place de ce type de structure n’est souvent pas si facile. Par exemple, il est important de s’assurer que le type d’un objet ne doit pas être interrogé à chaque fois avant le trai­te­ment effectif des données, car ce scénario ne serait ni efficace ni per­for­mant. En par­ti­cu­lier lorsque de nombreux objets primitifs ren­contrent des objets com­po­sites, il est re­com­mandé d’utiliser un patron composite. L’approche de con­cep­tion de logiciels permet aux clients de traiter les objets in­di­vi­duels et com­po­sites de manière uniforme en cachant leurs dif­fé­rences au client.

Qu’est-ce qu’un composite pattern ?

Le patron composite est l’un des 23 patrons de con­cep­tions pour le dé­ve­lop­pe­ment de logiciels publiés en 1994 par Erich Gamma, Richard Helm, Ralph Johnson et John Vlissides (connus sous le nom de GOF pour « Gang of Four »). À l’instar du patron de con­cep­tion façade et du dé­co­ra­teur, c’est l’un des patrons de con­cep­tion dont la fonction de base est de combiner des objets et des classes en struc­tures plus grandes.

Le composite pattern suit l’idée de base de re­pré­sen­ter des objets simples ainsi que leurs ré­ci­pients ou com­po­si­tions (c’est-à-dire des com­po­si­tions d’objets) dans une classe abstraite afin de pouvoir les traiter de manière uniforme. Une telle structure est également appelée hié­rar­chie partielle et globale (en anglais part-whole hierarchy), dans laquelle un objet n’est toujours qu’une partie d’un tout ou le tout (qui est constitué de parties in­di­vi­duelles).

Pour quels problèmes le composite pattern est-il une solution ?

L’objectif fon­da­men­tal du composite pattern est, comme pour tous les modèles GOF, de traiter au mieux les problèmes de con­cep­tion ré­cur­rents dans le dé­ve­lop­pe­ment orienté objet. Le résultat souhaité est un logiciel aussi souple que possible et ca­rac­té­risé par des objets faciles à mettre en œuvre, testables, échan­geables et réu­ti­li­sables. À cette fin, le patron composite décrit une manière de traiter de la même manière les objets simples et les objets com­po­sites. Il est ainsi possible de créer une structure d’objet facile à com­prendre et per­met­tant un accès au client le plus efficace possible. Il minimise également la tendance à l’erreur du code.

Composite pattern : re­pré­sen­ta­tion graphique (UML)

Afin de mettre en œuvre les hié­rar­chies complètes par­tielles efficaces men­tion­nées ci-dessus, le patron composite prévoit la mise en œuvre d’une interface de com­po­sants uniforme pour les objets partiels simples, également appelés leaf objects, et les objets com­po­sites. Les objets feuilles in­di­vi­duels intègrent di­rec­te­ment cette interface, les objets com­po­sites trans­met­tent au­to­ma­ti­que­ment les demandes concrètes des clients pour l’interface à leurs com­po­sants su­bor­don­nés. Pour le client, peu importe le type d’objet (partie ou ensemble), puisqu’il n’a qu’à s’adresser à l’interface.

Le diagramme de classes qui suit, en langage de mo­dé­li­sa­tion UML, explicite les con­nexions et les hié­rar­chies dans un logiciel basé sur un composite pattern.

Avantages et in­con­vé­nients du composite pattern

Le Composite Pattern est une constante dans le dé­ve­lop­pe­ment des logiciels. Ce sont surtout les projets dont les struc­tures sont fortement im­bri­quées qui bé­né­fi­cient de l’approche pratique de l’or­ga­ni­sa­tion des objets : qu’il s’agisse d’un objet primitif ou composite, avec des dé­pen­dances simples ou complexes : la pro­fon­deur et la largeur de l’im­bri­ca­tion n’ont en effet pas d’im­por­tance pour le modèle de con­cep­tion composite. Les dif­fé­rences entre les types d’objets peuvent être com­plè­te­ment ignorées par le client, de sorte qu’aucune fonction distincte n’est requise pour l’accès. Cela présente l’avantage que le code client reste simple et léger.

Un autre point fort du composite pattern design est la flexi­bi­lité et l’ex­ten­si­bi­lité facile que le modèle donne à un logiciel : l’interface uni­ver­selle des com­po­sants permet d’inclure de nouvelles feuilles et de nouveaux objets com­po­sites sans mo­di­fi­ca­tion du code - que ce soit du côté du client ou dans des struc­tures d’objets exis­tantes.

Bien que le modèle composite et son interface uniforme offrent un certain nombre d’avantages, cette solution n’est pas exempte d’in­con­vé­nients. L’interface, en par­ti­cu­lier, peut donner beaucoup de maux de tête aux dé­ve­lop­peurs. La mise en œuvre elle-même pose de grands défis, car il faut par exemple décider quelles opé­ra­tions doivent être définies spé­ci­fi­que­ment dans l’interface et les­quelles dans les classes com­po­sites. De plus, un ajus­te­ment ultérieur des pro­prié­tés du composite (par exemple, la res­tric­tion des éléments enfants autorisés) s’avère gé­né­ra­le­ment compliqué et difficile à mettre en œuvre.

Avantages In­con­vé­nients
Affiche les struc­tures d’objets fortement im­bri­quées La mise en œuvre de l’interface des com­po­sants est très difficile
Code de programme simple et facile à com­prendre Les ajus­te­ments ul­té­rieurs des pro­prié­tés du composite sont com­pli­qués et dif­fi­ciles à mettre en œuvre.
Bonne capacité d’expansion  

Scénarios d’ap­pli­ca­tion du modèle composite

L’uti­li­sa­tion du modèle composite est rentable partout où des opé­ra­tions doivent être ef­fec­tuées sur des struc­tures de données dy­na­miques dont la hié­rar­chie est d’une largeur et/ou d’une pro­fon­deur complexes. Dans ce cas, on parle également d’une structure ar­bo­res­cente binaire, qui est in­té­res­sante pour une grande variété de scénarios logiciels et qui est fré­quem­ment utilisée. Voici ci-dessous des exemples typiques.

Systèmes de fichiers : les com­po­sants les plus im­por­tants des logiciels des appareils com­pren­nent des systèmes de fichiers. Ces derniers peuvent être car­to­gra­phiés de manière optimale avec le modèle composite : les fichiers in­di­vi­duels en tant qu’objets feuilles et les dossiers, qui peuvent eux-mêmes contenir des fichiers ou des dossiers sup­plé­men­taires, en tant qu’objets com­po­sites.

Menus du logiciel : les menus de programme re­pré­sen­tent également un cas d’uti­li­sa­tion typique pour une structure ar­bo­res­cente binaire selon le composite pattern design. La barre de menu contient une ou plusieurs entrées racine (objets com­po­sites) comme « Fichier ». Ceux-ci donnent accès à divers éléments de menu, qui sont soit di­rec­te­ment cli­quables (Leaf), soit con­tien­nent des menus sub­di­vi­sés (composite).

In­ter­faces uti­li­sa­teur gra­phiques (GUI) : les struc­tures ar­bo­res­centes et le modèle composite peuvent également jouer un rôle important dans la con­cep­tion des in­ter­faces uti­li­sa­teur gra­phiques. Loin des simples éléments de feuille comme les boutons, les champs de texte ou les cases à cocher, le résumé des con­te­neurs com­po­sites comme les cadres ou les panneaux offre une structure claire et plus de clarté.

Exemple de code : modèle composite

Dans pra­ti­que­ment aucun autre langage de pro­gram­ma­tion, l’approche par modèle composite n’est aussi fermement établie qu’en Java. Entre autres choses, le modèle est également à la base de l’Abstract Window Toolkit (AWT), une API pratique et populaire com­pre­nant environ 50 classes Java prêtes à l’emploi pour le dé­ve­lop­pe­ment d’in­ter­faces uti­li­sa­teur Java mul­ti­pla­te­formes. Dans l’exemple de code suivant, basé sur un modèle composite sur Java, sur baeldung.com, nous avons donc décidé d’utiliser ce langage de pro­gram­ma­tion populaire.

Dans cet exemple est présentée la structure hié­rar­chique des services (de­part­ments) d’une en­tre­prise (company). Tout d’abord, l’interface du composant De­part­ment est définie ainsi :

public interface Department {
	void printDepartmentName();
}

Ensuite, les deux classes de feuilles simples Fi­nan­cial­De­part­ment (pour le dé­par­te­ment financier) et Sa­les­De­part­ment (pour le dé­par­te­ment com­mer­cial) sont définies. Les deux im­plé­men­tent la méthode print­De­part­ment­Name() de l’interface du composant, mais ne con­tien­nent pas d’autres objets de dé­par­te­ment.

public class FinancialDepartment implements Department {
	private Integer id;
	private String name;
	public void printDepartmentName() {
		System.out.println(getClass().getSimpleName());
	}
	// standard constructor, getters, setters
}
public class SalesDepartment implements Department {
	private Integer id;
	private String name;
	public void printDepartmentName() {
		System.out.println(getClass().getSimpleName());
	}
	// standard constructor, getters, setters
}

Enfin, une classe composite cor­res­pon­dant à la hié­rar­chie est définie avec Head­De­part­ment. Cette classe se compose de plusieurs éléments de dé­par­te­ment et contient des méthodes pour ajouter des éléments sup­plé­men­taires (add­De­part­ment) ou supprimer des éléments existants (re­mo­ve­De­part­ment) en plus de la méthode print­De­part­ment­Name :

public class HeadDepartment implements Department {
	private Integer id;
	private String name;
	private List<department> childDepartments;</department>
	public HeadDepartment(Integer id, String name) {
		this.id = id;
		this.name = name;
		this.childDepartments = new ArrayList<>();
	}
	public void printDepartmentName() {
		childDepartments.forEach(Department::printDepartmentName);
	}
	public void addDepartment(Department department) {
		childDepartments.add(department);
	}
	public void removeDepartment(Department department) {
		childDepartments.remove(department);
	}
}
Aller au menu principal