Le but du pattern Composite est de permettre de manipuler une collection d'objets appartenant à une même hiérarchie comme s'il s'agissait d'un objet unique de la classe mère.
Par exemple, considérons la hiérarchie d'objets graphiques ainsi que l'agrégat d'objets graphiques de la figure suivante :
Figure 2.1 Hiérarchie d'objets graphiques
Il serait agréable de pouvoir appliquer directement à un groupe d'objets graphiques les méthodes d'objet graphique lui même ! c'est précisément le but du pattern composite : proposer d'utiliser un tout comme s'il s'agissait d'une de ses parties.
Afin de pouvoir utiliser un groupe d'objets graphique comme un objet graphique, il faut qu'il dispose des mêmes méthodes. Pour cela, nous allons le faire dériver de la classe ObjetGraphique comme le montre la figure suivante :
Figure 2.2 Le pattern Composite
Il reste ensuite à surcharger la plupart des opérations afin qu'elles aient un comportement cohérent. Par exemple, la méthode Afficher du composite pourrait s'écrire ainsi si vous utilisiez un conteneur de la STL :
TypeCollection::iterator itCourant=collection.begin();
while (itCourant !=collection.end())
{
(*ifCourant)->Afficher();
itCourant++;
}
Nous obtenons donc une classe à l'aspect bizarroïde qui, à la fois, hérite de ObjetGraphique et agrège (le plus souvent par pointeurs) des membres de la classe ObjetGraphique.
Le principal avantage de Composite découle de la définition même de ce pattern. Comme il devient possible d'utiliser un agrégat comme un objet unique, nous allons pouvoir imbriquer ces agrégats !
Toutefois, il y a un inconvénient majeur. Supposons que vous ayez une collection d'objets graphiques dont certains sont, préciséments, des objets graphiques composites. Hors, vous souhaitez désagréger les composites afin de ramener tous les objets sur un même plan, la question qui se pose est la suivante :
Comment isoler les composites des objets atomiques pour leur appliquer une méthode spécifique à la classe AgrégatGraphique ?
Il y a deux réponses possibles à cette question :
La première idée consiste à remonter les méthodes ajouterUnObjet et dégrouper dans la classe ObjetGraphique. Ainsi, tout descendant d'ObjetGraphique est susceptible de les utiliser de manière polymorphique ce qui supprimer toute obligation de cast d'un pointeur d'ObjetGraphique vers AgrégatGraphique.
Reste néanmoins à spécifier le comportement par défaut de ces méthodes pour les classes auxquelles elles ne s'appliquent pas vraiment, là encore, deux écoles s'affrontent :
Quelque chose est toutefois génant dans cette approche : la nécessité de pourvoir certaines classes (par exemple Ligne et Cercle) de méthodes qui leur sont impropres comme dégrouper. Considérons dorénavant la seconde méthode qui pallie ce problème en en amenant toutefois un autre !
Je vous l'ai dit et répété, les downcast doivent être évités autant que possible. Il existe néanmoins une manière de les faire moins redoutable que les autres. En effet, le meilleur moyen d'appeler une méthode d'AgrégatGraphique à bon escient est de s'assurer que l'objet considéré est bel et bien de la classe AgrégatGraphique.
Supposons que la hiérarchie d'ObjetGraphique possède une méthode de prototype :
virtual AgrégatGraphique *ObjetGraphique::asAgrégatGraphique(void);
laquelle renvoie 0 si l'objet considéré n'est pas de classe AgrégatGraphique (comportement par défaut) et est redéfinie dans AgrégatGraphique pour renvoyer this.
Il devient alors évident de détecter quels objets sont de classe AgrégatGraphique !
Les classes de véhicules nécessaires au fonctionnement de ce TP sont décrites dans le fichier entête des tps.
Vous vous en doutez, nous allons vous demander de créer une classe ParcDeVéhicules qui soit un composite de Véhicule. Vous veillerez à proposer (dans deux fichiers différents), les deux implémentation possibles permettant d'appliquer sélectivement les méthodes propres au composite.
Pour réaliser l'agrégation, vous pouvez :
Bonne Chance !