Accès rapide :
Présentation du design pattern Factory Method
La représentation UML du design pattern Factory Method
Mise en oeuvre du design pattern Factory Method
Mise en oeuvre simplifiée du design pattern Singleton
Ce design pattern permet de créer une instance à partir d'une classe dérivée d'un type abstrait, selon un critère donné et en vous évitant de systématiquement d'avoir à évaluer le critère de choix. La classe exacte utilisée pour produire l'objet n'est donc pas connue par l'appelant.
Un exemple classique consiste à permettre la construction d'un objet d'image à partir de son nom de fichier : le problème étant que la manière de charger (ou sauvegarder) une image est dépendante de son type (gif, jpg, png...) et donc de l'extension du fichier.
Voici un diagramme de classe UML présentant le pattern Factory Method dans sa forme originelle.
Dans les faits, les développeurs simplifient souvent le modèle en retirant l'interface de fabrique : on ne considère plus que la classe d'implémentation de la factory, qui peut aussi être un singleton.
Une simplification encore plus forte consiste à transformer la méthode de fabrique en une méthode statique directement portées par l'interface de produit (nous testerons cette variante en fin de chapitre).
Nous allons appliquer ce design pattern à la manipulation d'images, comme introduit précédemment. Je vous propose de revoir le modèle UML pour l'adapter à notre besoin : le voici.
Commençons, tout d'abord, à nous pencher sur la définition abstraite du produit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
package fr.koor.samples; public abstract class Image { private String filename; /** Le constructeur de la classe Image */ public Image(String filename) { this.filename = filename; } /** Pour récupérer le nom du fichier associé à l'image */ public String getFilename() { return filename; } /** Permet de charger en mémoire l'image */ public abstract void loadImage(); /** Permet de sauvegarder l'image sur le disque */ public abstract void saveImage(); /** Un petit toString pour les affichages sur la console */ @Override public String toString() { String className = this.getClass().getName(); return className + ": Imaginez une superbe image : " + filename; } } |
Nous pouvons maintenant définir une classe dérivée pour chaque format d'images supporté par l'application.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package fr.koor.samples; public class ImagePng extends Image { public ImagePng(String filename) { super(filename); } @Override public void loadImage() { System.out.println( "Imaginez qu'on charge une image au format PNG" ); } @Override public void saveImage() { System.out.println( "Imaginez qu'on sauvegarde une image au format PNG" ); } } |
ImageGif
et ImageJpg
.
Voici maintenant la définition de notre interface de fabrique.
1 2 3 4 5 6 7 |
package fr.koor.samples; public interface ImageFactory { Image createImage( String filename ); } |
Voici maintenant la classe d'implémentation pour notre fabrique d'images.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package fr.koor.samples; public class ImageFactoryImpl implements ImageFactory { @Override public Image createImage(String filename) { String extention = filename.substring( filename.lastIndexOf(".") ); Image image = switch( extention.toLowerCase() ) { case ".gif" -> new ImageGif( filename ); case ".jpg" -> new ImageJpg( filename ); case ".png" -> new ImagePng( filename ); default -> throw new RuntimeException( "Format " + extention + " not supported" ); }; image.loadImage(); return image; } } |
Et enfin, voici un petit exemple d'utilisation de notre « factory »
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package fr.koor.samples; public class Start { public static void main(String[] args) { ImageFactory factory = new ImageFactoryImpl(); Image image1 = factory.createImage( "essai.png" ); Image image2 = factory.createImage( "essai.jpg" ); System.out.println( image1 ); System.out.println( image2 ); } } |
Enfin, voici les résultats affichés par ce programme.
Imaginez qu'on charge une image au format PNG Imaginez qu'on charge une image au format JPG fr.koor.samples.ImagePng: Imaginez une superbe image : essai.png fr.koor.samples.ImageJpg: Imaginez une superbe image : essai.jpg
Notez bien que dans l'exemple proposé dans la section précédente, l'interface de fabrique ne nous ait pas réellement utile et comme son implémentation
ne fournit que la méthode de construction d'objet, on peut simplifier les choses en ramenant cette méthode directement sur le type Image
.
Voici comment vous pourriez procéder pour implémenter cette variation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
package fr.koor.samples; public abstract class Image { private String filename; /** Le constructeur de la classe Image */ public Image(String filename) { this.filename = filename; } /** Notre factory method, sous forme de méthode statique */ public static Image createImage(String filename) { String extention = filename.substring( filename.lastIndexOf(".") ); Image image = switch( extention.toLowerCase() ) { case ".gif" -> new ImageGif( filename ); case ".jpg" -> new ImageJpg( filename ); case ".png" -> new ImagePng( filename ); default -> throw new RuntimeException( "Format " + extention + " not supported" ); }; image.loadImage(); return image; } /** Pour récupérer le nom du fichier associé à l'image */ public String getFilename() { return filename; } /** Permet de charger en mémoire l'image */ public abstract void loadImage(); /** Permet de sauvegarder l'image sur le disque */ public abstract void saveImage(); /** Un petit toString pour les affichages sur la console */ @Override public String toString() { String className = this.getClass().getName(); return className + ": Imaginez une superbe image : " + filename; } } |
Dans cette variation, nous n'avons plus besoin des fichiers ImageFactory.java
et ImageFactoryImpl.java
: vous pouvez les
supprimer. Et voici maintenant comment invoquer notre « factory method ».
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
package fr.koor.samples; public class Start { public static void main(String[] args) { Image image1 = Image.createImage( "essai.png" ); Image image2 = Image.createImage( "essai.jpg" ); System.out.println( image1 ); System.out.println( image2 ); } } |
Les résultats produits par de nouveau main
devraient être les mêmes que précédemment.
Améliorations / Corrections
Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.
Emplacement :
Description des améliorations :