Factory

La programmation avec design patterns

Ce site ne sera plus alimenté de contenu après août 2014. Tous les nouveaux articles seront redigés pour www.waitingforcode.com
Souvent la méthode d'instantiation d'un objet est une bonne pratique pour maintenir le code évolutif et comprehénsible. Dans cet article on verra une méthode très simple qui permet de mettre en place ce mécanisme, le patron de conception appelé fabrique.

Factory pattern est analogique à une usine dans laquelle on peut produire de différents modèles de voiture d'une même marque. Dans cette illustration on retrouve au moins 3 acteurs :
- l'usine qui produit une voiture.
- la voiture qui détermine ce qui est fabriquée. Grâce à elle on sait que l'usine ne produit pas de jouets, mais bien les voitures.
- le modèle X de la voiture qui est le résultat final et concret de la fabrication.

Si l'on transporte cette illustration dans le mode de développement des applications, on peut dire que :
- l'usine est une classe qui fabrique des objets.
- la voiture est une interface qui définit ce que doit être retourné par la classe de fabrique.
- le modèle X de la voiture est l'objet retourné par la classe de fabrique. En plus, cet objet implémente l'interface (= est une voiture). Pour saisir mieux le concept, vous pouvez consulter les articles consacrés au polymorphisme. L'un des articles sur le polymorphisme dans Java se trouve sur ce site, dans la partie programmation orientée objet.

Exemple du Pattern Factory
Regardons maintenant ce design pattern sur le cas d'un exemple concret. Imaginons que l'on gère un site internet qui doit se connecter à un serveur distant pour télécharger les images. Cependant, les images pour de différentes versions linguistiques sont placées sur de différents serveurs. Cependant, dans notre code on ne peut pas placer des if-else partout pour détecter la classe qui va charger les images. Par contre, on peut définir une fabrique qui, en fonction de la langue, retournera l'instance de la bonne classe. Voici l'exemple :

// interface returned by factory
public interface ImageResourceInterface{
public String getImagePath();

public void setImagePath(String imagePath);
}

// 2 implementations of ImageResourceInterface
public class PolishImageResourceImpl implements ImageResourceInterface{
private String imagePath;

@Override
public String getImagePath(){
return "http://cdn.mysite.pl/"+this.imagePath;
}

@Override
public void setImagePath(final String imagePath){
this.imagePath=imagePath;
}

@Override
public String toString(){
return "PolishImageResourceImpl : { imagePath : "+getImagePath()+" }";
}
}

public class FrenchImageResourceImpl implements ImageResourceInterface{
private String imagePath;

@Override
public String getImagePath(){
return "http://cdn.mysite.fr/"+this.imagePath;
}

@Override
public void setImagePath(final String imagePath){
this.imagePath=imagePath;
}

@Override
public String toString(){
return "FrenchImageResourceImpl : { imagePath : "+getImagePath()+" }";
}
}

Les exemples ne sont pas compliqués. Un setter et un getter s'occupent de manipuler l'attribut imagePath. On observe que le getter possède l'adresse du serveur différent. Ensuite, pour faciliter l'exécution de l'exemple, la méthode toString() retourne le nom de la classe choisie par la classe d'usine. D'ailleurs, la voici :

public class ImageResourceFactory{
public static ImageResourceInterface getImageResourceImpl(final String country) throws Exception{
ImageResourceInterface imgResource;
if(country!=null&&country.equals("pl")){
imgResource=new PolishImageResourceImpl();
}
else if(country!=null&&country.equals("fr")){
imgResource=new FrenchImageResourceImpl();
}
else throw new Exception("Unknown resource implementation");
return imgResource;
}
}

Elle aussi est très simple. Sa seule méthode, getImageResourceImpl(), retourne l'instance de la classe implémentant l'interface ImageResourceInterface ou lance une exception si aucune implémentation ne peut être trouvée. Voici comment se présente son utilisation :

public class FactoryTest{
@Test
public void test(){
ImageResourceInterface imgResource;
// Firstly, select images location for French website
try{
imgResource=ImageResourceFactory.getImageResourceImpl("fr");
imgResource.setImagePath("images/fr/");
assertEquals("http://cdn.mysite.fr/images/fr/", imgResource.getImagePath());
System.out.println("Instance of "+imgResource);
}
catch(final Exception e){
System.out.println("An error occured on initializing ImageResourceInterface : "+e.getMessage());
}
// Secondly, select images location for Polish website
try{
imgResource=ImageResourceFactory.getImageResourceImpl("pl");
imgResource.setImagePath("images/pl/");
assertEquals("http://cdn.mysite.pl/images/pl/", imgResource.getImagePath());
System.out.println("Instance of "+imgResource);
}
catch(final Exception e){
System.out.println("An error occured on initializing ImageResourceInterface : "+e.getMessage());
}
}
}

Sur l'écran on verra bien :

Instance of FrenchImageResourceImpl : { imagePath : http://cdn.mysite.fr/images/fr/ }
Instance of PolishImageResourceImpl : { imagePath : http://cdn.mysite.pl/images/pl/ }


Quand utiliser Factory pattern ?
Comme on peut constater, la fabrique est très utile dans les situations où l'on veut se garantir une flexibilité dans le code. On peut imaginer que dans la première phase de développement d'une application, on utilise la classe X pour la gestion des fichiers XML. Cependant, 3 ans après une nouvelle classe, Y, plus performante et plus abordable, peut voir le jour. Alors pour remplacer X par Y, il suffira juste de modifier le bon endroit dans la classe de fabrique. Cette classe peut, par exemple, retourner l'instance qui traitera les fichiers HTML, une autre pour les fichiers XML et encore une troisième pour les fichiers compréssés.

En plus, ce patron de conception trouve son utilité dans le monde des frameworks où l'on peut vouloir laisser au développeur la possibilité de surcharger une classe par défaut. Par exmeple, on peut lui permettre d'utiliser une classe différente pour la gestion des sessions que celle qui est implémentée nativement dans le système. Il suffit alors que le développeur détermine sa classe dans la classe de fabrique.

Factory pattern est donc une méthode qui renforce l'encapsulation de la création d'un objet. Il permet d'instancer des objets étant d'un type abstrait. C'est pourquoi il fait partie d'une famille des patrons de conception créationnels.
Bartosz KONIECZNY 08-09-2013 17:37 design patterns
Moi

Développeur d'applications Internet et journaliste passionné par l'adjectif français. Un aigle polonais orienté vers la progression, volant très haut et écoutant du zouk après les matches du foot français.

Vous appréciez mon travail ?

Pour contribuer au développement de ce site, ou pour remercier pour des articles rédigés, vous pouvez faire un don.

Un conseil MySQL

Comment rajouter un 0 final au champ FLOAT ?

Parfois, pour afficher correctement un prix, on adopte le formate avec deux chiffres après le séparateur. On a donc 4,30 à la place de 4,3. Comment atteindre cela dans MySQL ? Il faut utiliser la méthode ROUND(). A priori elle se charge d'arrodinr un chiffre. Si l'on spécifie juste un seul chiffre, dans notre cas ROUND(4.3), on recevra un résultat avec arrondi (4). Cependant, quand on définit un deuxième paramètre qui correspond à la quantité des caractères après le séparateur, on verra un résultat "après le virgule". L'appel ROUND(4.3, 2) va alors nous retourner 4.30.