IUT Villetaneuse – M3105 : Conception et Programmation Objet Avancées (2016/2017)
Correction TD5 : Le Design Pattern Composite
Nous utilisons le design pattern composite pour représenter et calculer des expressions arithmétiques contenant des additions, soustractions, multiplications et divisions.
Une expression arithmétique peut être représentée sous forme d’arborescence. Par exemple, l’expression (3 + 4) / (5 * (10 – 6)) correspond à l’arborescence suivante :
Figure 1 – Arborescence
Dans un premier temps, nous n’utilisons pas le design pattern composite.
Sur l’arborescence précédente, on observe qu’une opération possède deux types d’opérandes : les nombres (feuilles) et les opérations (nœuds). Par exemple, dans l’opération 5 * (10 – 6), la première opérande est le nombre 5 et la seconde est l’opération (10 – 6).
a) Compléter le diagramme des classes pour représenter les deux arguments des opérations
Le diagramme doit inclure les classes suivantes :
- Nombre : classe concrète représentant un nombre avec un attribut
valeurNombre. - Operation : classe abstraite avec deux attributs
operande1etoperande2, tous deux de typeExpression.
Un commentaire doit préciser qu’une opération a exactement deux opérandes.
b) Ajouter les méthodes valeur() et toString() aux classes
Les classes doivent implémenter :
valeur(): méthode retournant une valeur de typedouble.toString(): méthode redéfinie pour afficher la représentation textuelle.
II. Design Pattern Composite
On introduit une classe Expression dont les classes Nombre et Operation héritent.
a) Modifier le diagramme avec le design pattern composite
La classe Expression devient la racine commune. Les méthodes getOperande1() et getOperande2() sont ajoutées avec un comportement par défaut (retour de null) pour les nombres.
b) Compléter le diagramme avec les spécifications suivantes
- Nombre : attribut
valeurNombreet constructeur initialisant ce dernier. - Expression : méthodes
getOperande1()etgetOperande2()retournant les opérandes (par défautnull). - Operation : constructeur initialisant les deux opérandes
operande1etoperande2, et redéfinition des méthodesgetOperande1()etgetOperande2().
c) Implémentation des méthodes valeur() et toString() pour la classe Addition
valeur() :
public double valeur() {
return this.getOperande1().valeur() + this.getOperande2().valeur();
}
toString() :
public String toString() {
return "(" + this.getOperande1() + " + " + this.getOperande2() + ")";
}
III. Traduction en Java et Tests
Voici le code Java correspondant au design pattern composite pour les expressions arithmétiques :
/**
* Gestion d'une expression arithmétique
*/
public abstract class Expression {
public Expression getOperande1() {
return null;
}
public Expression getOperande2() {
return null;
}
public abstract double valeur();
public abstract String toString();
}
public class Nombre extends Expression {
private double valeurNombre;
public Nombre(double uneValeur) {
this.valeurNombre = uneValeur;
}
public double valeur() {
return this.valeurNombre;
}
public String toString() {
return Double.toString(this.valeurNombre);
}
}
public abstract class Operation extends Expression {
private Expression operande1;
private Expression operande2;
public Operation(Expression op1, Expression op2) {
this.operande1 = op1;
this.operande2 = op2;
}
public Expression getOperande1() {
return this.operande1;
}
public Expression getOperande2() {
return this.operande2;
}
}
public class Addition extends Operation {
public Addition(Expression op1, Expression op2) {
super(op1, op2);
}
public double valeur() {
return this.getOperande1().valeur() + this.getOperande2().valeur();
}
public String toString() {
return "(" + this.getOperande1() + " + " + this.getOperande2() + ")";
}
}
public class Soustraction extends Operation {
public Soustraction(Expression op1, Expression op2) {
super(op1, op2);
}
public double valeur() {
return this.getOperande1().valeur() - this.getOperande2().valeur();
}
public String toString() {
return "(" + this.getOperande1() + " - " + this.getOperande2() + ")";
}
}
public class Multiplication extends Operation {
public Multiplication(Expression op1, Expression op2) {
super(op1, op2);
}
public double valeur() {
return this.getOperande1().valeur() * this.getOperande2().valeur();
}
public String toString() {
return "(" + this.getOperande1() + " * " + this.getOperande2() + ")";
}
}
public class Division extends Operation {
public Division(Expression op1, Expression op2) {
super(op1, op2);
}
public double valeur() {
return this.getOperande1().valeur() / this.getOperande2().valeur();
}
public String toString() {
return "(" + this.getOperande1() + " / " + this.getOperande2() + ")";
}
}
public class Calculatrice {
public static void main(String[] args) {
Expression trois = new Nombre(3);
Expression quatre = new Nombre(4);
Expression cinq = new Nombre(5);
Expression six = new Nombre(6);
Expression dix = new Nombre(10);
Expression s = new Soustraction(dix, six);
System.out.println(s + " = " + s.valeur());
Expression a = new Addition(trois, quatre);
System.out.println(a + " = " + a.valeur());
Expression m = new Multiplication(cinq, s);
System.out.println(m + " = " + m.valeur());
Expression d = new Division(a, m);
System.out.println(d + " = " + d.valeur());
}
}
FAQ
1. Pourquoi utiliser le design pattern composite pour les expressions arithmétiques ?
Le design pattern composite permet de traiter uniformément les objets simples (nombres) et complexes (opérations) comme des éléments d’une même hiérarchie. Cela simplifie la manipulation et le calcul des expressions, car toutes les classes héritent d’une interface commune (Expression).
2. Comment fonctionne la méthode valeur() dans la classe Nombre ?
La méthode valeur() de la classe Nombre retourne simplement la valeur stockée dans l’attribut valeurNombre, car un nombre est une expression simple dont la valeur est déjà définie.
3. Pourquoi les méthodes getOperande1() et getOperande2() retournent-elles null par défaut dans Expression ?
Dans le design pattern composite, les nombres n’ont pas d’opérandes. Les méthodes retournent null par défaut pour indiquer qu’elles ne sont pas applicables aux objets simples (Nombre). Les opérations, elles, redéfinissent ces méthodes pour retourner leurs opérandes.