- Composite (Motif de conception)
-
Objet composite
En génie logiciel, un objet composite est un patron de conception (design pattern) structurel.
Sommaire
Motivation
En programmation objet, un objet composite est constitué d'un ou de plusieurs objets similaires (ayant des fonctionnalités similaires). L'idée est de manipuler un groupe d'objets de la même façon que s'il s'agissait d'un seul objet. Les objets ainsi regroupés doivent posséder des opérations communes, c'est-à-dire un "dénominateur commun".
Quand l'utiliser
Vous avez l'impression d'utiliser de multiples objets de la même façon, souvent avec des lignes de code identiques ou presque. Par exemple, lorsque la seule et unique différence entre deux méthodes est que l'une manipule un objet de type Carré, et l'autre un objet Cercle. Lorsque, pour le traitement considéré, la différenciation n'a pas besoin d'exister, il serait plus simple de considérer l'ensemble de ces objets comme homogène.
Structures
- Component (composant)
- déclare l'interface pour la composition d'objet
- met en œuvre le comportement par défaut
- déclare une interface pour l'accès aux composants enfants
- Leaf (feuille)
- représente des objets feuille dans la composition
- Composite
- définit un comportement pour les composants ayant des enfants
- stocke les composants enfants
- met en œuvre la gestion des enfants de l'interface Component
- Client
- manipule les objets de la composition à travers l'interface Composite
Exemple C++
//Cet exemple represente la hiérarchie d'un système de fichiers : fichiers/répertoires class Composant { //L'objet abstrait qui sera 'composé' dans le composite //Dans notre exemple, il s'agira d'un fichier ou d'un répertoire public: //Parcours récursif de l'arbre //(Utilisez plutôt le Design Pattern "Visiteur" à la place de cette fonction) virtual void ls_r() = 0 ; protected: std::string name; }; class File : public Composant { //Leaf public: void ls_r() { std::cout << name << std::endl; } }; class Repertoire : public Composant { //Composite //Le repertoire est aussi un 'composant' car il peut être sous-répertoire protected: std::list<Composant*> files; //List des composants public: void ls_r() { std::cout << "[" << name << "]" << std::endl; for( std::list<Composant*>::iterator it = files.begin(); it != files.end(); it ++ ) { it->ls_r(); } } };
Exemple Java
L'exemple qui suit, écrit en Java, met en œuvre une classe graphique qui peut être ou bien une ellipse ou une composition de différents graphiques. Chaque graphique peut être imprimé.
Il pourrait être étendu en y ajoutant d'autres formes (rectangle etc) et méthodes (translation etc).
import java.util.ArrayList; interface Graphic { //Imprime le graphique. public void print(); } class CompositeGraphic implements Graphic { //Collection de graphiques enfants. private ArrayList<Graphic> mChildGraphics = new ArrayList<Graphic>(); //Imprime le graphique. public void print() { for (Graphic graphic : mChildGraphics) { graphic.print(); } } //Ajoute le graphique à la composition composition. public void add(Graphic graphic) { mChildGraphics.add(graphic); } //Retire le graphique de la composition. public void remove(Graphic graphic) { mChildGraphics.remove(graphic); } } class Ellipse implements Graphic { //Imprime le graphique. public void print() { System.out.println("Ellipse"); } } public class Program { public static void main(String[] args) { //Initialise quatre ellipses Ellipse ellipse1 = new Ellipse(); Ellipse ellipse2 = new Ellipse(); Ellipse ellipse3 = new Ellipse(); Ellipse ellipse4 = new Ellipse(); //Initialise three graphiques composites CompositeGraphic graphic = new CompositeGraphic(); CompositeGraphic graphic1 = new CompositeGraphic(); CompositeGraphic graphic2 = new CompositeGraphic(); //Composes les graphiques graphic1.add(ellipse1); graphic1.add(ellipse2); graphic1.add(ellipse3); graphic2.add(ellipse4); graphic.add(graphic1); graphic.add(graphic2); //Imprime le graphique complet (quatre fois la chaîne "Ellipse"). graphic.print(); } }
Exemple en PHP 5
<?php class Component { // Attributes private $basePath; private $name; private $parent; public function __construct($name, CDirectory $parent = null) { // Debug : echo "constructor Component"; $this->name = $name; $this->parent = $parent; if($this->parent != null) { $this->parent->addChild($this); $this->basePath = $this->parent->getPath(); } else { $this->basePath = ''; } } // Getters public function getBasePath() { return $this->basePath; } public function getName() { return $this->name; } public function getParent() { return $this->parent; } // Setters public function setBasePath($basePath) { $this->basePath = $basePath; } public function setName($name) { $this->name = $name; } public function setParent(CDirectory $parent) { $this->parent = $parent; } // Method public function getPath() { return $this->getBasePath().'/'.$this->getName(); } } class CFile extends Component { // Attributes private $type; public function __construct($name, $type, CDirectory $parent = null) { // Debug : echo "constructor CFile"; $this->type = $type; // Retrieve constructor of Component parent::__construct($name, $parent); } // Getters public function getType() { return $this->type; } // Setters public function setType($type) { $this->type = $type; } // Methods of Component class public function getName() { return parent::getName().'.'.$this->getType(); public function getPath() { return parent::getPath().'.'.$this->getType(); } } class CDirectory extends Component { // Attributes private $childs; public function __construct($name, CDirectory $parent = null) { // Debug : echo "constructor CDirectory"; $this->childs = array(); // Retrieve constructor of Component parent::__construct($name, $parent); } // Getters public function getChilds() { return $this->childs; } // Setters public function setChilds($childs) { $this->childs = $childs; } // Methods public function addChild(Component $child) { $child->setParent($this); $this->childs[] = $child; } public function showChildsTree($level = 0) { echo "<br/>".str_repeat(' ', $level).$this->getName(); foreach($this->getChilds() as $child) { if($child instanceof self) { $child->showChildsTree($level+1); } else { echo "<br/>".str_repeat(' ', $level+1).$child->getName(); } } } } ?>
Exemple d'utilisation (example of use):
<?php $root = new CDirectory('root'); $dir1 = new CDirectory('dir1', $root); $dir2 = new CDirectory('dir2', $root); $dir3 = new CDirectory('dir3', $root); $dir4 = new CDirectory('dir4', $dir2); $file1 = new CFile('file1','txt', $dir1); $file2 = new CFile('doc', 'pdf', $dir4); $root->showChildsTree(); ?>
résultat à l'écran (result on the screen) :
root dir1 file1.txt dir2 dir4 doc.pdf dir3
Exemple C#
Produisant un résultat similaire à l'exemple en php, une variante en C#.
Dans ce code la méthode Draw() correspond à la méthode opération() du diagramme de classes.
/// <summary> Par simplicité, ni méthode Add ni Remove ni GetChild. </summary> abstract class Composant { public int Level = 0; public string Nom; public virtual void Draw() { for (var i = 0; i < Level; i++) Console.Write(" "); } } class Feuille : Composant { public override void Draw() { base.Draw(); Console.WriteLine("Feuille : {0}", Nom); } } class Composite : Composant { public Composant[] Composants; // serait private s'il y avait une méthode Add. public override void Draw() { base.Draw(); Console.WriteLine("Composite : {0}", Nom); foreach (Composant composant in Composants) { composant.Level = this.Level + 1; composant.Draw(); } } } class Program { static void Main(string[] args) { //__________________________________________________________________________ // On crée en général la structure par de multiples appels à la méthode Add. var cadre= new Composite() { Nom = "fond d écran", Composants = new Composant[] { new Composite() { Nom = "ciel", Composants = new Composant[] { new Feuille() { Nom="soleil" }}}, new Composite() { Nom = "mer", Composants = new Composant[] { new Composite() { Nom="bateau", Composants = new Composant[] { new Feuille() { Nom="kevin" }, new Feuille() { Nom="katsumi" }}}}}} }; //__________________________________________________________________________ // Et voilà le pourquoi de l'utilisation du pattern: // un seul appel à Draw dessine tout l'écran. cadre.Draw(); } }
Et le résultat
Composite : fond d écran Composite : ciel Feuille : soleil Composite : mer Composite : bateau Feuille : kevin Feuille : katsumi
Catégorie : Patron de conception - Component (composant)
Wikimedia Foundation. 2010.