I. Exploration▲
Imaginons que nous ayons à gérer un petit magasin, lequel offre à ses clients la possibilité d'acheter des pièces détachées, des ensembles préassemblés ou même des ensembles plus complets faits de plusieurs ensembles préassemblés. Par exemple, nous vendons des équipements et vêtements sportifs, soit en pièces détachées (chaussures, pantalon de survêtement, poids d'haltères…), soit en ensemble (le survêtement complet, l'haltère avec un jeu de 5 paires de poids…), soit enfin des packages complets (haltère + survêtement + serviette). Pour chaque article, qu'il soit composé ou en pièces détachées, nous connaissons son prix, un nom descriptif et un code-barre.
Le décor étant planté, voyons comment nous pouvons nous organiser. Tout d'abord, quelques détails fonctionnels :
- le prix d'un ensemble est calculé suivant la méthode suivante : c'est la somme des prix de ses parties (composées ou non) moins 10% ;
- le code-barre est spécifique à chaque produit vendu qu'il soit composé ou non ;
- le nom descriptif d'un ensemble contient à la fois un descriptif de l'ensemble et le descriptif des parties
Nous nous pencherons ici sur le back office de notre magasin, c'est-à-dire sur l'aide à la composition de produits pour le vendeur. Chaque produit a un ensemble de propriétés qui lui sont propres, par exemple le poids d'haltère a un poids, le rameur un encombrement, etc. ce qui explique que je leur fournisse une description (classe) à chacun.
Il n'en reste pas moins que l'interface générique servira à la composition et que nous préférons donc au niveau de ce back office, disposer d'un moyen unique de composition, et de manipulation, c'est là que le pattern intervient.
II. Structure▲
Notre structure doit donc disposer d'un point d'entrée générique : le produit, de feuilles pour notre arborescence : Les pièces détachées et d'objets composites.
III. Implémentation▲
Reprenons donc notre exemple, mais simplifié, je vais disposer de deux classes : barre (d'haltère) et poids et je montrerai une composition : l'haltère complète (poids + barre).
/*
* Produit.java
*
* Created on 12 mars 2003, 18:35
*/
package
com.developpez.composite;
/**
*
*
@author
smeric
*/
public
interface
Produit {
/**
* Fournit le prix du produit.
* Peut être calculé.
*/
public
float
getPrix
(
);
/**
* Code barre unique d'un produit.
*/
public
String getCodeBarre
(
);
/**
* Descriptif du produit. Peut être le résultat d'une accumulation de
* descriptifs si le produit est composé
*/
public
String getDescriptif
(
);
}
/*
* halterre.java
*
* Created on 12 mars 2003, 18:56
*/
package
com.developpez.composite;
/**
*
*
@author
smeric
*/
public
class
Barre implements
Produit {
/** Creates a new instance of haltere */
public
Barre
(
) {
}
/** Code barre unique d'un produit.
*/
public
String getCodeBarre
(
) {
return
codeBarre;
}
/** Descriptif du produit. Peut être le résultat d'une accumulation de
* descriptifs si le produit est composé
*/
public
String getDescriptif
(
) {
return
descriptif;
}
/** Fournit le prix du produit.
* Peut être calculé.
*/
public
float
getPrix
(
) {
return
prix;
}
/** Getter for property longueur.
*
@return
Value of property longueur.
*/
public
float
getLongueur
(
) {
return
this
.longueur;
}
/** Setter for property longueur.
*
@param
longueur
New value of property longueur.
*/
public
void
setLongueur
(
float
longueur) {
this
.longueur =
longueur;
}
private
float
poids =
0
;
private
float
prix =
0
;
private
String codeBarre;
private
String descriptif;
/** Holds value of property longueur. */
private
float
longueur;
}
/*
* Poids.java
*
* Created on 12 mars 2003, 18:39
*/
package
com.developpez.composite;
/**
*
*
@author
smeric
*/
public
class
Poids implements
Produit {
/** Creates a new instance of Poids */
public
Poids
(
) {
}
public
float
getPoids
(
) {
return
poids;
}
/** Code barre unique d'un produit.
*/
public
String getCodeBarre
(
) {
return
codeBarre;
}
/** Descriptif du produit. Peut être le résultat d'une accumulation de
* descriptifs si le produit est composé
*/
public
String getDescriptif
(
) {
return
descriptif;
}
/** Fournit le prix du produit.
* Peut être calculé.
*/
public
float
getPrix
(
) {
return
prix;
}
private
float
poids =
0
;
private
float
prix =
0
;
private
String codeBarre;
private
String descriptif;
}
/*
* ProduitException.java
*
* Created on 13 mars 2003, 10:59
*/
package
com.developpez.composite;
import
java.lang.RuntimeException;
/**
*
*
@author
smeric
*/
public
class
ProduitException extends
RuntimeException {
public
ProduitException
(
) {
super
(
);
}
public
ProduitException
(
String msg) {
super
(
msg);
}
public
ProduitException
(
Exception e) {
super
(
e);
}
}
/*
* ProduitComposite.java
*
* Created on 12 mars 2003, 19:01
*/
package
com.developpez.composite;
import
java.util.*;
/**
* Classe d'implémentation d'un produit composite pour notre magasin.
*
@author
smeric
*/
public
class
ProduitComposite implements
Produit {
/** Creates a new instance of ProduitComposite */
public
ProduitComposite
(
) {
children =
new
ArrayList
(
);
}
/** Ajoute un produit à la liste des composants
*
@param
produit
le produit que l'on veut ajouter au composite
*
@throws
ProduitException
Si le produit est null.
*/
public
void
add
(
Produit produit) throws
ProduitException {
assert
null
!=
children;
if
(
null
==
produit) {
throw
new
ProduitException
(
"Impossible d'ajouter un produit null"
);
}
children.add
(
produit);
}
/** Enlève un produit de la composition.
*
@param
produit
le produit à retirer de la composition.
*
@throws
ProduitException
Si le produit est null ou n'est pas dans la liste.
*/
public
void
remove
(
Produit produit) throws
ProduitException {
assert
null
!=
children;
if
(
null
==
produit) {
throw
new
ProduitException
(
"Impossible d'enlever un produit null"
);
}
if
(
!
children.contains
(
produit)) {
throw
new
ProduitException
(
"Impossible d'enlever le produit, il n'est pas dans la liste"
);
}
children.remove
(
produit);
}
/** Renvoie la liste des composantes du produit sous la forme d'un itérateur.
<
p
>
* Voir le pattern itérateur.
*
@return
La liste des composantes.
*/
public
Iterator getChildren
(
) {
assert
null
!=
children;
return
children.iterator
(
);
}
/** Code barre unique d'un produit.
*
@return
le code barre du produit
*/
public
String getCodeBarre
(
) {
return
codeBarre;
}
/** Descriptif du produit. Peut être le résultat d'une accumulation de
* descriptifs si le produit est composé
*
@return
le descriptif composé
*/
public
String getDescriptif
(
) {
assert
null
!=
children;
StringBuffer result =
new
StringBuffer
(
);
result.append
(
descriptif);
result.append
(
" : ("
);
for
(
Iterator i =
children.iterator
(
); i.hasNext
(
); ) {
Object objet =
i.next
(
);
assert
null
!=
objet : "Un objet null a été trouvé dans la liste des composantes"
;
assert
objet instanceof
Produit : "Un
\"
non produit
\"
a été trouvé dans la liste des composantes"
;
Produit composant =
(
Produit)objet;
result.append
(
composant.getDescriptif
(
));
if
(
i.hasNext
(
)) {
// on ajoute une virgule pour séparer les descriptifs
result.append
(
", "
);
}
}
result.append
(
" )"
);
return
result.toString
(
);
}
/** Fournit le prix du produit.
* Peut être calculé.
*
@return
le prix calculé
*/
public
float
getPrix
(
) {
float
result =
0
;
for
(
Iterator i =
children.iterator
(
); i.hasNext
(
); ) {
Object objet =
i.next
(
);
assert
null
!=
objet : "Un objet null a été trouvé dans la liste des composantes"
;
assert
objet instanceof
Produit : "Un
\"
non produit
\"
a été trouvé dans la liste des composantes"
;
Produit composant =
(
Produit)objet;
result +=
composant.getPrix
(
);
}
result =
result *
0.9
f; // soit une réduction de 10 %
return
result;
}
private
Collection children;
private
String codeBarre;
private
String descriptif;
}
Et voici un exemple d'utilisation
Barre maBarre =
new
Barre
(
);
maBarre.setPrix
(
25
f);
maBarre.setDescriptif
(
"Barre d'haltérophilie"
);
maBarre.setCodeBarre
(
"BA0001"
);
maBarre.setLongueur
(
150
f);
Poids leger =
new
Poids
(
);
leger.setPrix
(
15
f);
leger.setDescriptif
(
"Poids d'haltère"
);
leger.setCodeBarre
(
"PA0001"
);
leger.setPoids
(
0.5
f);
Poids moyen =
new
Poids
(
);
moyen.setPrix
(
17
f);
moyen.setDescriptif
(
"Poids d'haltère"
);
moyen.setCodeBarre
(
"PA0002"
);
moyen.setPoids
(
1
f);
Poids lourd =
new
Poids
(
);
lourd.setPrix
(
19
f);
lourd.setDescriptif
(
"Poids d'haltère"
);
lourd.setCodeBarre
(
"PA0003"
);
lourd.setPoids
(
1.5
f);
ProduitComposite haltere =
new
ProduitComposite
(
);
haltere.add
(
maBarre);
haltere.add
(
leger);
haltere.add
(
moyen);
haltere.add
(
lourd);