Friday, October 12, 2012

Patron : Fabrique abstraite (Abstract Factory)

Le patron fabrique abstraite est de type création.

Il se base sur la patron fabrique mais ajoute une couche d'abstraction de plus. Ce patron permet de regrouper des fabriques qui ont un theme commun sans spécifier les classes concretes.



Dans le code ci-dessous, voit qu'il y a 2 fabriques qui font plus ou moins la meme chose. Alors on les regroupe et on crée la classe AbstractFactoryGuerrier.
On peut constater que dans la classe Jeu, on ne spécifie jamais "new Katana()" car la responsabilité de créer un katana se trouve dans la fabrique.



Jeu.java
public class Jeu {

 public static void main(String[] args) {
  Jeu jeu = new Jeu();
  jeu.jouer();
 }
 
 public void jouer() {
  AbstractFactoryGuerrier factory = createFactory("Lancelot");
  preparerArme(factory);
  preparerArmure(factory);
  
  factory = createFactory("Musashi");
  preparerArme(factory);
  preparerArmure(factory);
 }
 
 // on cree la factory correspondante au "type" recu en parametre
 public AbstractFactoryGuerrier createFactory(String type) {
  System.out.println("\n   Factory pour " + type);
  
  if("Lancelot".equals(type))
   return new FactoryChevalier();
  else
   return new FactorySamourai();
 }
 
 // on prepare une arme (que ce soit une epee ou un katana)
 public void preparerArme(AbstractFactoryGuerrier factory) {
  Arme arme = factory.creerArme();
  arme.attaquer();
 }
 
 // on prepare une armure (que ce soit une cuirasse ou une yoroi)
 public void preparerArmure(AbstractFactoryGuerrier factory) {
  Armure armure = factory.creerArmure();
  armure.proteger();
 }
}
AbstractFactoryGuerrier.java
abstract class AbstractFactoryGuerrier {
 abstract Armure creerArmure();
 abstract Arme creerArme();
}
FactoryChevalier.java
public class FactoryChevalier extends AbstractFactoryGuerrier{
 Armure creerArmure() {
  return new Cuirasse();
 }

 Arme creerArme() {
  return new Epee();
 }
}
FactorySamourai.java
public class FactorySamourai extends AbstractFactoryGuerrier{
 Armure creerArmure() {
  return new Yoroi();
 }

 Arme creerArme() {
  return new Katana();
 }
}
Arme.java
abstract class Arme {
 abstract void attaquer();
}
Epee.java
public class Epee extends Arme{
 void attaquer() {
  System.out.println("Epee pour transperser les armures");
 }
}
Katana.java
public class Katana extends Arme{
 void attaquer() {
  System.out.println("Katana tranche un cheveu en deux");
 }
}
Armure.java
abstract class Armure {
 abstract void proteger();
}
Cuirasse.java
public class Cuirasse extends Armure{
 void proteger() {
  System.out.println("Cuirasse lourde mais protectrice");
 }
}
Yoroi.java
public class Yoroi extends Armure{
 void proteger() {
  System.out.println("Yoroi souple qui permet de se déplacer facilement");
 }
}


Et le résultat de l'éxecution:
Factory pour Lancelot
Epee pour transperser les armures
Cuirasse lourde mais protectrice 

Factory pour Musashi
Katana tranche un cheveu en deux
Yoroi souple qui permet de se déplacer facilement

Thursday, October 11, 2012

Patron : Prototype


Le patron prototype est de type création.
Il est utilisé lorsque la création d'un objet est complexe et consommatrice de temps. Pour cela, on crée juste une instance et on la clone.
Le fait de ne pas utiliser de "new Xxx()" et d'initialiser des propriétés fait gagner beaucoup de temps.

Admettons que nous sommes Saroumane et on veut construire une armée de monstres (orques et wargs) le plus rapidement possible pour combattre Sauron.
On peut faire naitre des orques ("Montre x = new Monstre()") mais cela prendrait beaucoup de temps, car on devra attendre qu'ils atteignent l'age adulte pour pouvoir se battre.
L'autre solution, et de prendre l'orque le plus fort et de le cloner! C'est beaucoup plus rapide!


Ici, la classe Monstre est une sous-classe de Cloneable et doit posséder une fonction "clone()".

Magicien.java
import java.util.ArrayList;
import java.util.Iterator;

public class Magicien {
 public static void main(String[] args) {
  Magicien saroumane = new Magicien();
  saroumane.construireArmee();
 }
 
 public Magicien() { }
 
 public void construireArmee() {
  Monstre orque = new Orque();
  orque.setName("Chef orque");
  Monstre warg = new Warg();
  orque.setName("Chef warg");
  
  Monstre monstreCloned;
  ArrayList armee = new ArrayList();
  
  // on va creer 10 soldats : 5 orques et 5 wargs
  for(int i = 0; i < 10; i++) {
   if(i%2 == 0) {
    monstreCloned = orque.clone();
    monstreCloned.setName("soldat orque " + i);
   } else {
    monstreCloned = warg.clone();
    monstreCloned.setName("soldat warg " + i);
   }
   
   armee.add(monstreCloned);
  }
  
  Iterator it =  armee.iterator();
  
  while(it.hasNext()){
   monstreCloned = it.next();
   System.out.println(monstreCloned.getName());
  }
 }
}
//ignorer les 3 balises ci-dessous
Monstre.java
public abstract class Monstre implements Cloneable{
 protected String name;
 
 public Monstre clone() {
  try {
   Monstre clone = (Monstre)super.clone();
   clone.name = this.name;
   return clone;
  } catch (CloneNotSupportedException e){
   return null;
  }
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }
}
Orque.java
public class Orque extends Monstre{ }

Warg.java
public class Warg extends Monstre{ }


Le résultat de l'execution est:

soldat orque 0
soldat warg 1
soldat orque 2
soldat warg 3
soldat orque 4
soldat warg 5
soldat orque 6
soldat warg 7
soldat orque 8
soldat warg 9

Et voila, maintenant Saroumane est prêt à partir en guerre!

Wednesday, October 10, 2012

Patron : Monteur (Builder)

Le patron monteur (builder en anglais) est de type création.

Ce patron à pour but de séparer la construction d'un objet complexe de sa representation. Ainsi le meme processus de création peut créer d'autres representations.

En gros, cela veut dire que la classe qui construit l'objet n'est pas la même que la classe qui utilise l'objet.
Voici le diagramme UML provenant de Wikipedia.

Voici le diagramme UML de l'exemple utilisé ci-dessous.


Ici on peut imaginer Asterix qui veut une potion magique. Alors il demande au druide Panoramix de lui préparer.
Asterix n'a aucune idée de la preparation de la potion magique.
Attention, car dans Asterix et le coup du menhir, Panoramix est devenu fou et il a créé une potion qui fait voler les gens.

Voici le code.

Jeu.java
public class Jeu {
 
 public static void main(String[] args) {
  Jeu jeu = new Jeu();
  jeu.jouer();
 }
 
 public Jeu() { }
 
 public void jouer() {
  Recette builderPotionMagique = new RecettePotionMagique();
  Druide panoramix = new Druide(builderPotionMagique);
  
  panoramix.preparerPotion();
  
  Potion potionMagique = panoramix.getPotion();
  System.out.print("Potion magique -> ");
  potionMagique.afficherListeIngredients();
  
  Recette builderPotionVolante = new RecettePotionVolante();
  Druide panoramixFou = new Druide(builderPotionVolante);
  // Asterix et le coup du menhir
  panoramixFou.preparerPotion();
  Potion potionVolante = panoramixFou.getPotion();
  System.out.print("Potion volante -> ");
  potionVolante.afficherListeIngredients();
 }
}
Druide.java
C'est cette classe qui contient les étapes de créations de l'objet Potion.
public class Druide {
 private Recette recette;
 
 public Druide(Recette recette) {
  this.recette = recette;
 }
 
 public Potion getPotion() {
  return recette.getPotion();
 }
 
 public void preparerPotion() {
  recette.ajouterHerbe();
  recette.ajouterHomard();
  recette.ajouterCarotte();
  recette.ajouterSel();
  recette.ajouterPoisson();
 }
}
Recette.java
public abstract class Recette {
 public abstract Potion getPotion();
 public abstract void ajouterHerbe();
 public abstract void ajouterHomard();
 public abstract void ajouterCarotte();
 public abstract void ajouterSel();
 public abstract void ajouterPoisson();
}
RecettePotionMagique.java
Cette classe contient les quantités exactes des ingrédients nécessaires pour faire une potion magique.
public class RecettePotionMagique extends Recette {
 private Potion potion;
 
 public RecettePotionMagique() {
  potion = new Potion();
 }
 
 public Potion getPotion() {
  return potion;
 }
 
 public void ajouterHerbe() {
  potion.setHerbe("3 brindilles");
 }

 public void ajouterHomard() {
  potion.setHomard("1 homard");
 }

 public void ajouterCarotte() {
  potion.setCarotte("2 carottes");
 }

 public void ajouterSel() {
  potion.setSel("3 pincés");
 }

 public void ajouterPoisson() {
  potion.setPoisson("1 poisson frais");
 }
}
RecettePotionVolante.java
public class RecettePotionVolante extends Recette {
 private Potion potion;
 
 public RecettePotionVolante() {
  potion = new Potion();
 }
 
 public Potion getPotion() {
  return potion;
 }
 
 public void ajouterHerbe() {
  potion.setHerbe("1 brindille");
 }

 public void ajouterHomard() {
  potion.setHomard("3 homard");
 }

 public void ajouterCarotte() {
  potion.setCarotte("4 carottes");
 }

 public void ajouterSel() {
  potion.setSel("1 pincés");
 }

 public void ajouterPoisson() {
  potion.setPoisson("2 poissons pas frais");
 }
}
Potion.java
Cette classe est le produit qui sera créé.
public class Potion {
 private String herbe;
 private String homard;
 private String carotte;
 private String sel;
 private String poisson;
 
 public void setHerbe(String herbe) {
  this.herbe = herbe;
 }
 public void setHomard(String homard) {
  this.homard = homard;
 }
 public void setCarotte(String carotte) {
  this.carotte = carotte;
 }
 public void setSel(String sel) {
  this.sel = sel;
 }
 public void setPoisson(String poisson) {
  this.poisson = poisson;
 }
 
 public void afficherListeIngredients() {
  System.out.println("Ingrédients: " + herbe + ", " + homard + ", " + carotte + ", " + sel + ", " + poisson);
 }
}

Ce qui sera affiché :

  • Potion magique -> Ingrédients: 3 brindilles, 1 homard, 2 carottes, 3 pincés, 1 poisson frais 
  • Potion volante -> Ingrédients: 1 brindille, 3 homard, 4 carottes, 1 pincés, 2 poissons pas frais


Imaginons maintenant qu'il existe une autre potion. Et bien il suffit d'ajouter une nouvelle recette.
Par exemple:
public class RecettePotionMiniature extends Recette { ... }


Et si on n'utilise pas le patron builder ?

Et bien les étapes de création de la potion magique seraient dans la class Jeu. La classe Jeu deviendrait longue et aurait trop de responsabilités. La classe Jeu serait plus difficile à maintenir.

Friday, October 5, 2012

Patron : Singleton


Singleton est un patron de programmation de type creation.

Il permet de s'assurer qu'il existe qu'une seule instance d'une classe.
Par exemple, dans une partie de jeu de role (Donjons et Dragons ou autre), il existe un seul maitre du jeu.
Alors si on tente de créer un programme, il serait intéressant de limiter le nombre de maitre de jeu à 1.

Les étapes:

  • créer un constructeur "private" car on ne veut pas de constructeur "public". Attention, quand on crée une classe en java sans constructeur, en fait il y en a un d'ajouter automatiquement qui est "public". Donc en créant notre constructeur "private", on override le constructeur "public"
  • créer une variable statique qui va detenir l'instance unique
  • créer une fonction qui retourne l'instance unique 



Jeu.java
public class Jeu {
 
 public static void main(String[] args) {
  Jeu jeu = new Jeu();
  jeu.jouer();
 }
 
 public Jeu() { }
 
 public void jouer() { 
  Singleton maitreDuJeu = Singleton.getInstance();
  
  System.out.println(maitreDuJeu.getCompteur());
  maitreDuJeu.setCompteur(23);
  System.out.println(maitreDuJeu.getCompteur());
  
  // Singleton autreMaitreDuJeu = new Singleton();
  // -> erreur de compilation car il n'y a
  //    pas de constructeur "public"
  
  Singleton autreMaitreDuJeu = Singleton.getInstance();
  // en fait c'est le meme objet (= meme instance)!
  System.out.println(autreMaitreDuJeu.getCompteur());
 }
}
Singleton.java
public class Singleton {

 private static final Singleton instanceUnique = new Singleton();
 private int compteur = 0;

 private Singleton() {
  // besoin d'avoir un constructeur
  // "private" pour empecher d'avoir
  // un constructeur "public"
 }

 public static Singleton getInstance() {
  return instanceUnique;
 }
 
 public int getCompteur() {
  return compteur;
 }

 public void setCompteur(int compteur) {
  this.compteur = compteur;
 }
 
}
Le résultat de ce programme sera:
0
23
23
L'objet autreMaitreDuJeu est le même que maitreDuJeu, donc c'est normal d'avoir 23 comme résultat.

Et sans l'utilisation du patron ?

Prenons le même code sans utiliser le patron Singleton et imaginons que 2 ans plus tard, quelqu'un doit le modifier.

 public void jouerSansPatronSingleton() {
//on utilise le constructeur publique
  Singleton maitreDuJeu = new Singleton();
  
  System.out.println(maitreDuJeu.getCompteur());
  maitreDuJeu.setCompteur(23);
  System.out.println(maitreDuJeu.getCompteur());
  
// 2 ans plus tard, quelqu'un ajoute ces lignes
// car il ne sait pas qu'il existe deja un objet
  Singleton autreMaitreDuJeu = new Singleton();
  System.out.println(autreMaitreDuJeu.getCompteur());
 }
Le résultat de ce programme sera:
0
23
0
Donc quand on veut s'assurer qu'il existe une seule instance d'une classe, le patron Singleton est très utile.

Patron : Fabrique (factory)

Le patron Fabrique (Factory en anglais) est un patron de type conception.

Ce patron contient une classe qui fait appel à une fabrique pour créer des objets sans connaitre la classe exacte de ces objets. Pour déterminer la classe exacte, un paramètre est passé lors de l'appel.



Description des classes :

  • Fabrique : la fabrique en charge de créer les objets selon le paramètre recu 
  • Race : classe abstraite 
  • Humain, Orc, Elf : classes fille de Race 
  • Jeu : classe principale qui ne se preocupe pas de quel type de race sont les objets qu'elle manipule 



Dans l'exemple ci-dessous, la classe Jeu fait appel à la classe Fabrique. La classe Jeu ne connais pas les sous-classes de Race.
La responsabilité de créer le bon objet est laissé à la classe Fabrique. Cela fait moins de ligne de code pour la classe Jeu.

Une fois les objets personnagePrincipal, ennemi et ami crées, la classe Jeu traite ces objets comme des objets de type Race.


Jeu.java
public class Jeu {
 
 public static void main(String[] args) {
  Jeu jeu = new Jeu();
  jeu.jouer();
 }
 
 public Jeu() { }
  public void jouer() {
  Fabrique fabrique = new Fabrique();
  
  Race personnagePrincipal = fabrique.getRace("humain");
  action(personnagePrincipal);
  
  Race ennemi = fabrique.getRace("orc");
  action(ennemi);
  
  Race ami = fabrique.getRace("elf");
  action(ami);
 }
 public void action(Race joueur) {
  //on ne se préocupe pas si "joueur" 
  //est un humain ou un orc ou un elf
  joueur.parler();
 }
}
Race.java
public abstract class Race {
  abstract void parler();
}
Humain.java
public class Humain extends Race{
 public Humain() {}
 
 public void parler() {
  System.out.println("Je suis un humain!");
 }
}
Orc.java
public class Orc extends Race{
 public Orc() {}
 
 public void parler() {
  System.out.println("Grrrrrr");
 }
}
Elf.java
public class Elf extends Race{
 public Elf() {}
 
 public void parler() {
  System.out.println("Je suis un elf!");
 }
}
Fabrique.java
public class Fabrique {
 public Fabrique() { }
 
 public Race getRace(String type) {
  Race race;
  
  // ...
  // par exemple: doit faire des trucs 
  // compliqués ici avant la creation d'objet 
  // (verification, initialisation, etc.)
  // ...
  
  if ("humain".equals(type))
   race = new Humain();
  else if ("orc".equals(type))
   race = new Orc();
  else if ("elf".equals(type))
   race = new Elf();
  else
   race = null;
  
  return race;
 }
}



Et sans la Fabrique...

L'exemple est très simple, donc ce n'est pas évident de prouver que sans l'utilisation de ce patron, le code devient plus complexe. Mais je vais essayer.
Voici maintenant le meme code sans la fabrique.
public class NoFactory {

 /**
  * @param args
  */
 public static void main(String[] args) {
  NoFactory noFactory = new NoFactory();
  noFactory.start();
 }
 public NoFactory() { }
 
 public void start() {
  
  // ...
  // par exemple: doit faire des trucs 
  // compliqués ici avant la creation d'objet 
  // (verification, initialisation, etc.)
  // ...
  
  Race personnagePrincipal = new Humain();
  action(personnagePrincipal);
  
  Race ennemi = new Orc();
  action(ennemi);
  
  Elf ami = new Elf();
  action(ami);
 }
 
 public void action(Race joueur) {
  // on ne se préocupe pas si "joueur" 
  // est un humain ou un orc ou un elf
  joueur.parler();
 }
}
Deux choses :

  1. la classe NoFactory doit maintenant s'occuper de faire des trucs compliqués (exemple: verifications, initialisation,etc.) avant la creation des objets 
  2. la classe NoFactory doit connaitre le type exacte des objets. 


J'ai certainement fait des fautes ou écrit des choses pas clair, donc n'hésitez pas laisser un commentaire. Lien Wikipedia : Fabrique