Exercices corrigés sur les patrons de conception (Design Patterns)
Exercice I : Factory et Abstract Factory
Exos déjà corrigés Exercice I (Factory et Abstract Factory)
Exercice II : Singleton
Voici une implémentation corrigée du patron Singleton en Java :
Problème de concurrence dans le Singleton
Si deux threads exécutent le test en même temps et s'il n'existe pas encore d'objet Database, ils pourraient tous deux passer la condition if (singleObject == null). Cela signifie que les deux threads créeront un objet Database.
Exercice IV : Pattern Builder
Voici une implémentation corrigée du patron Builder pour la création de pizzas :
Classe abstraite MonteurPizza
abstract class MonteurPizza {
protected Pizza pizza;
public Pizza getPizza() {
return pizza;
}
public void creerNouvellePizza() {
pizza = new Pizza();
}
public abstract void monterPate();
public abstract void monterSauce();
public abstract void monterGarniture();
}
Classe Pizza
class Pizza {
private String pate = "";
private String sauce = "";
private String garniture = "";
public void setPate(String pate) {
this.pate = pate;
}
public void setSauce(String sauce) {
this.sauce = sauce;
}
public void setGarniture(String garniture) {
this.garniture = garniture;
}
}
Classe MonteurPizzaPiquante
class MonteurPizzaPiquante extends MonteurPizza {
public void monterPate() {
pizza.setPate("feuilletée");
}
public void monterSauce() {
pizza.setSauce("piquante");
}
public void monterGarniture() {
pizza.setGarniture("saucisson");
}
}
Classe Serveur
class Serveur {
private MonteurPizza monteurPizza;
public void setMonteurPizza(MonteurPizza mp) {
monteurPizza = mp;
}
public Pizza getPizza() {
return monteurPizza.getPizza();
}
public void construirePizza() {
monteurPizza.creerNouvellePizza();
monteurPizza.monterPate();
monteurPizza.monterSauce();
monteurPizza.monterGarniture();
}
}
Classe Client
class Client {
public static void main(String[] args) {
Serveur serveur = new Serveur();
MonteurPizza monteurPizzaPiquante = new MonteurPizzaPiquante();
serveur.setMonteurPizza(monteurPizzaPiquante);
serveur.construirePizza();
Pizza pizza = serveur.getPizza();
}
}
Exercice VII : Pattern Composite
Voici une implémentation corrigée du patron Composite pour gérer des produits d'haltérophilie :
Interface Produit
public interface Produit {
public float getPrix();
public String getDescriptif();
}
Classe Barre
public class Barre implements Produit {
private float prix = 0;
private String descriptif;
private float longueur;
public Barre() {}
public String getDescriptif() {
return descriptif;
}
public float getPrix() {
return prix;
}
public float getLongueur() {
return this.longueur;
}
public void setLongueur(float longueur) {
this.longueur = longueur;
}
}
Classe Poids
public class Poids implements Produit {
private float poids = 0;
private float prix = 0;
private String descriptif;
public Poids() {}
public float getPoids() {
return poids;
}
public String getDescriptif() {
return descriptif;
}
public float getPrix() {
return prix;
}
}
Classe ProduitComposite
public class ProduitComposite implements Produit {
private ArrayList altere = new ArrayList();
private String descriptif;
public void ajouter(Produit produit) {
altere.add(produit);
}
public void remove(Produit produit) {
if (altere.contains(produit)) {
altere.remove(produit);
}
}
public Iterator getChildren() {
return altere.iterator();
}
public String getDescriptif() {
StringBuffer result = new StringBuffer();
result.append(descriptif);
result.append(" : (");
for (Produit produit : altere) {
result.append(produit.getDescriptif());
if (produit instanceof ProduitComposite) {
result.append(", ");
}
}
result.append(")");
return result.toString();
}
public float getPrix() {
float result = 0;
for (Produit produit : altere) {
result += produit.getPrix();
}
result = result * 0.9f; // réduction de 10%
return result;
}
}
Classe Program
class Program {
public static void main(String[] args) {
Barre maBarre = new Barre();
maBarre.setPrix(25f);
maBarre.setDescriptif("Barre d'haltérophilie");
maBarre.setLongueur(150f);
Poids leger = new Poids();
leger.setPrix(15f);
leger.setDescriptif("Poids léger d'haltère");
leger.setPoids(0.5f);
Poids moyen = new Poids();
moyen.setPrix(17f);
moyen.setDescriptif("Poids moyen d'haltère");
moyen.setPoids(1f);
Poids lourd = new Poids();
lourd.setPrix(19f);
lourd.setDescriptif("Poids lourd d'haltère");
lourd.setPoids(1.5f);
ProduitComposite haltere = new ProduitComposite();
haltere.setDescriptif("Haltère complet");
haltere.ajouter(maBarre);
haltere.ajouter(leger);
haltere.ajouter(moyen);
haltere.ajouter(lourd);
}
}
Exercice VIII : Pattern Composite (2011)
Voici une implémentation corrigée du patron Composite pour gérer une structure de fichiers et répertoires :
Interface AbstractFile
public interface AbstractFile {
public void afficher();
}
Classe Repertoire
public class Repertoire implements AbstractFile {
private String name;
private ArrayList fichiers = new ArrayList();
private Retrait retrait;
public Repertoire(String name, Retrait retrait) {
this.name = name;
this.retrait = retrait;
}
public void ajouter(AbstractFile f) {
fichiers.add(f);
}
public void afficher() {
System.out.println(retrait.getRetrait() + name);
retrait.augmenterRetrait();
for (AbstractFile fichier : fichiers) {
fichier.afficher();
}
retrait.diminuerRetrait();
}
}
Classe Fichier
public class Fichier implements AbstractFile {
private String name;
private Retrait retrait;
public Fichier(String name, Retrait retrait) {
this.name = name;
this.retrait = retrait;
}
public void afficher() {
System.out.println(retrait.getRetrait() + name);
}
}
Classe Retrait
public class Retrait {
private StringBuffer sbRetrait = new StringBuffer();
public String getRetrait() {
return sbRetrait.toString();
}
public void augmenterRetrait() {
sbRetrait.append("___");
}
public void diminuerRetrait() {
if (sbRetrait.length() >= 3) {
sbRetrait.setLength(sbRetrait.length() - 3);
}
}
}
Classe Program
public class Program {
public static void main(String[] args) {
Retrait retrait = new Retrait();
Repertoire repertoire3 = new Repertoire("rép3", retrait);
Repertoire repertoire2 = new Repertoire("rép2", retrait);
Repertoire repertoire1 = new Repertoire("rép1", retrait);
Fichier a = new Fichier("a", retrait);
Fichier b = new Fichier("b", retrait);
Fichier c = new Fichier("c", retrait);
Fichier d = new Fichier("d", retrait);
Fichier e = new Fichier("e", retrait);
repertoire3.ajouter(a);
repertoire3.ajouter(repertoire2);
repertoire3.ajouter(b);
repertoire2.ajouter(c);
repertoire2.ajouter(d);
repertoire2.ajouter(repertoire1);
repertoire1.ajouter(e);
repertoire3.afficher();
}
}
Exercice IX : Pattern Composite (2010)
Voici une implémentation corrigée du patron Composite pour gérer des graphiques :
Interface Graphic
public interface Graphic {
public void print();
}
Classe CompositeGraphic
public class CompositeGraphic implements Graphic {
private ArrayList mChildGraphics = new ArrayList();
public void print() {
for (Graphic graphic : mChildGraphics) {
graphic.print();
}
}
public void add(Graphic graphic) {
mChildGraphics.add(graphic);
}
public void remove(Graphic graphic) {
mChildGraphics.remove(graphic);
}
}
Classe Cercle
public class Cercle implements Graphic {
public void print() {
System.out.println("Cercle");
}
}
Classe Program
public class Program {
public static void main(String[] args) {
Cercle cercle1 = new Cercle();
Cercle cercle2 = new Cercle();
Cercle cercle3 = new Cercle();
Cercle cercle4 = new Cercle();
CompositeGraphic graphic1 = new CompositeGraphic();
CompositeGraphic graphic2 = new CompositeGraphic();
CompositeGraphic graphic = new CompositeGraphic();
graphic1.add(cercle1);
graphic1.add(cercle2);
graphic1.add(cercle3);
graphic2.add(cercle4);
graphic.add(graphic1);
graphic.add(graphic2);
graphic.print();
}
}
Exercice sur le pattern Adapter (Class Adapter)
Voici une implémentation corrigée du patron Adapter pour convertir des tensions électriques :
Interface SocketAdapter
public interface SocketAdapter {
public Volt get120Volt();
public Volt get12Volt();
public Volt get3Volt();
}
Classe Volt
public class Volt {
private int volts;
public Volt(int v) {
this.volts = v;
}
public int getVolts() {
return volts;
}
public void setVolts(int volts) {
this.volts = volts;
}
}
Classe Socket
public class Socket {
public Volt getVolt() {
return new Volt(120);
}
}
Classe SocketClassAdapterImpl
public class SocketClassAdapterImpl extends Socket implements SocketAdapter {
@Override
public Volt get120Volt() {
return getVolt();
}
@Override
public Volt get12Volt() {
Volt v = getVolt();
return convertVolt(v, 10);
}
@Override
public Volt get3Volt() {
Volt v = getVolt();
return convertVolt(v, 40);
}
private Volt convertVolt(Volt v, int i) {
return new Volt(v.getVolts() / i);
}
}
Classe AdapterPatternTest
public class AdapterPatternTest {
public static void main(String[] args) {
testClassAdapter();
}
private static void testClassAdapter() {
SocketAdapter sockAdapter = new SocketClassAdapterImpl();
Volt v3 = sockAdapter.get3Volt();
Volt v12 = sockAdapter.get12Volt();
Volt v120 = sockAdapter.get120Volt();
System.out.println("v3 volts using Class Adapter=" + v3.getVolts());
System.out.println("v12 volts using Class Adapter=" + v12.getVolts());
System.out.println("v120 volts using Class Adapter=" + v120.getVolts());
}
}
FAQ sur les patrons de conception
1. Qu'est-ce qu'un design pattern ?
Un design pattern est une solution réutilisable à un problème de conception récurrent dans le développement de logiciels. Il permet de structurer le code pour le rendre plus lisible, maintenable et extensible.
2. Pourquoi utiliser le patron Singleton ?
Le patron Singleton est utilisé pour garantir qu'une classe n'a qu'une seule instance et fournir un point d'accès global à cette instance. Cela est utile pour gérer des ressources partagées comme une base de données ou un service de journalisation.
3. Quelle est la différence entre le pattern Factory et le pattern Abstract Factory ?
Le pattern Factory crée des instances d'une seule classe, tandis que le pattern Abstract Factory crée des familles de classes apparentées. Abstract Factory est utilisé pour créer des interfaces de haut niveau pour des objets qui ont une relation complexe.