Composite

La programmation avec design patterns

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
Certaines situations en développement nécessitent la participation de beaucoup de petits objets dans la création d'un objet final. Le caractère répétitif des situations a permis d'établir une règle qui n'est rien d'autre qu'un design pattern appelé composite.

Composite (l'objet composé) permet à plusieurs objets ayant un comportement commun de se ressembler et de créer un grand objet final. Cet objet final aura toujours les mêmes caractéristiques que ses composants. Grâce à ce court descriptif on peut facilement déduire qu'on a à voir avec un patron de conception de famille structurelle. Les composants conçoivent une nouvelle structure.

L'objet composé est une réponse idéale à toute problématique de représentation d'une structure hiérarchique et relationnelle. Les relations entre les objets se font selon le principe has a (a) et is a (est). Elles sont d'ordre 1 à plusieurs. Cela se traduit par le fait qu'un objet a beasoin de plusieurs d'autres objets pour fonctionner. Et ces plusieurs d'autres objets sont susceptibles de faire partie d'un autre objet.

Vu que l'objet composé est en principe le même que ses composants, on peut traiter les deux exactement de la même manière. Au total, 4 familles d'acteurs participent dans composite :
- component : il s'agit d'une couche d'abstraction définissant le noeud et l'objet composé.
- leaf (un noeud) : cet acteur correspond aux objets qui ne peuvent pas contenir d'autres noeuds.
- composite (l'objet composé) : c'est le coeur de ce patron de conception. L'objet composé, contrairement à un noeud, peut, et même doit, contenir un ou plusieurs noeuds.
- client : c'est la classe client qui manipule les objets hiérarchiques.

Dans le monde réel on peut rencontrer composite un peu partout. Prenons l'exemple d'une boutique qui vend des vêtements. Les vêtements peuvent être vendus séparément ou en packages. Même si proprement parlant, ce n'est pas la même chose (une chemise != une chemise + un jean), c'est bien l'exemple du composite. Pourquoi ? Tout simplement parce que les deux ont des caractéristiques identiques. Une tenue possède un prix, un code barre, un sac adapté. Pareil le package (une chemise + un jean) qu'on vend à un prix donné, qu'on scanne par son code barre et qu'on met dans un sac d'une taille adaptée.

Exemple du composite
Néanmoins, dans notre exemple on utilisera une illustration venant de l'Internet. La création des tableaux en HTML se fait par l'injection des colonnes (td) dans les lignes (tr) qui elles-mêmes sont injectées dans les corps de contenu (tbody). D'après ce descriptif on voit que notre code aura deux objets composés : une ligne et un corps de contenu.


class Tbody implements HtmlElement{
List<HtmlElement> nodes=new ArrayList<HtmlElement>();

@Override
public String getOpenedTag(){
return "<tbody>";
}

@Override
public String getClosedTag(){
return "</tbody>";
}

public void addNode( HtmlElement node){
this.nodes.add(node);
}

public String getTbodyStructure(){
StringBuilder tbody=new StringBuilder(getOpenedTag());
for( HtmlElement element : this.nodes){
tbody.append(element.getOpenedTag());
if(element.getNodes()!=null){
for( HtmlElement item : element.getNodes()){
tbody.append(item.getOpenedTag());
tbody.append(item.getClosedTag());
}
}
tbody.append(element.getClosedTag());
}
tbody.append(getClosedTag());
return tbody.toString();
}

@Override
public List<HtmlElement> getNodes(){
return this.nodes;
}
}

class Tr implements HtmlElement{
List<HtmlElement> nodes=new ArrayList<HtmlElement>();

public void addNode( HtmlElement node){
this.nodes.add(node);
}

@Override
public String getOpenedTag(){
return "<tr>";
}

@Override
public String getClosedTag(){
return "</tr>";
}

@Override
public List<HtmlElement> getNodes(){
return this.nodes;
}
}

Ci-dessus nos 2 objets composés. On remarque la présence d'une liste privée stockant des noeuds de l'objet. En occurrence il s'agit des éléments HTML que l'objet en question peut contenir.


interface HtmlElement{
public String getOpenedTag();

public String getClosedTag();

public List<HtmlElement> getNodes();
}

class Td implements HtmlElement{
@Override
public String getOpenedTag(){
return "<td>";
}

@Override
public String getClosedTag(){
return "</td>";
}

@Override
public List<HtmlElement> getNodes(){
return null;
}
}

L'interface HtmlElement présente l'acteur qu'on a appelé component. Il s'agit d'une définition des noeuds et des objets composés. Plus bas, la classe Td, représente une colonne du tableau. Contrairement aux classes Tr et Tbody, Td ne contient aucune collection lui permettant de stocker les noeuds.


Tr tr=new Tr();
tr.addNode(new Td());
tr.addNode(new Td());
Tbody tbody=new Tbody();
tbody.addNode(tr);
System.out.println("Table body structure is : "+tbody.getTbodyStructure());

Et voici le code du client qui crée deux composites : Tr et Tbody. Un grand avantage qu'on voit immédiatement est la clarté. Le client ne fait que créer les instances des classes et tout le traitement se fait dans les composites. Même si demain on imagine de rajouter un composite Table, qui englobera Tbody, Tr et Td, l'évolution se fera du côté des composites et non pas du code client.

Voici le résultat du traitement du client :

Table body structure is : <tbody><tr><td></td><td></td></tr></tbody>


Comme on a évoqué précédemment, composite permet de créer les objets à partir de plusieurs autres objets. Vu qu'ils sont tous les mêmes caractéristiques, ils peuvent subir les mêmes opérations. Cela facilite non pas seulement la création mais aussi l'évolution de ces classes.
Bartosz KONIECZNY 06-10-2013 16:18 design patterns
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil Symfony1

Comment récupérer un paramètre de la requête dans le template ?

Il suffit d'utiliser la méthode get() de l'objet $sf_params, par exemple: $sf_params->get('nom_du_parametre').

Comme c'est déjà le cas dans les actions, on peut également préciser la valeur par défaut, au cas où le paramètre n'existe pas. Exemple de l'utilisation: $sf_params->get('nom_du_parametre', 'valeur par défaut');