Design patterns : le motif Factory

Romain Guy (Gfx)

Les motifs de conception servent à offrir un catalogue de solutions objets, à identifier ces solutions pour faciliter le dialogue entre architectes logiciel et pour tirer pleinement parti des caractéristiques de la programmation objet.

- Titre : Design patterns : le motif Factory

- Auteur : Romain Guy (Gfx)

- Parution : 24 septembre 2008

- Mise à jour : 17 juin 2020

- Licence :

Sommaire

- Introduction

- I. Exemples de motifs

- II. Les motifs de création

- II-A. Application exemple

- II-B. Introduction d'une fabrique

- III. Code source

- IV. Remerciements

Introduction

De nombreuses méthodes existent pour simplifier la phase de conception des logiciels. Parmi les plus connues, considérons Merise et UML. Mais une autre méthode existe, plus proche de l'implémentation. Lors de la conception d'une application, de nombreux problèmes peuvent survenir. Le système des Design Patterns, ou motifs de conception, représente un système objet destiné à la résolution de problèmes techniques.

Un design pattern constitue un petit ensemble de classes apte à offrir la solution la plus efficace à un problème. La définition d'un motif de conception repose donc sur trois critères. Premièrement, le problème à résoudre est classique et bien connu. Ensuite, l'ensemble des classes employées porte un nom unique (on parle par exemple du motif « Decorator »). Enfin, la solution que propose ce motif correspond à la résolution la plus optimale du problème.

Les designs patterns proviennent du travail de nombreux développeurs qui se sont tour à tour penchés sur les mêmes problèmes. En mettant en corrélation ces travaux, on a pu désigner les meilleures solutions découvertes sous le terme de motifs de conception. La connaissance de ces motifs permet au programmeur de trouver rapidement des implémentations pour ses programmes.

La principale difficulté réside dans l'identification du problème et dans sa mise en relation avec des motifs connus.

I - Exemples de motifs

L'ouvrage de référence sur les motifs de conception se nomme « Design Patterns », de Gamma, Helm, Johnson et Vlissides. Ces auteurs sont plus communément cités sous le nom du Gang Of Four (GOF). Dans cette œuvre, 23 motifs de conception se voient détaillés et analysés. En voici quelques-uns :

- l'association modèle-vue-contrôleur (MVC), qui dissocie le traitement des données de leur représentation graphique au sein des interfaces utilisateurs. Ce motif est employé à outrance au sein de l'API Java Swing ;

- l'observateur permet à un objet de recevoir des notifications lorsque l'objet observé subit des modifications. Ici encore l'API Swing contient de nombreux exemples d'utilisation de ce motif ;

- l'itérateur sert à parcourir une liste d'objets homogènes ;

- la fabrique qui instancie des objets depuis une classe particulière. Ce motif s'emploie souvent lorsque les constructeurs ne sont pas publics. Plusieurs exemples se retrouvent dans le JDK sous les noms de *Factory.

Les motifs de conception connus se groupent en trois catégories. La première incarne les motifs de création qui réalisent des instances de classes pour vous. Le second groupe concerne les motifs structuraux servant à composer de larges structures d'objets (comme les interfaces graphiques). Enfin la troisième catégorie regroupe les motifs de comportement aidant à la communication entre les objets du système.

Ces cours se focaliseront chacun sur un motif bien particulier avec une mise en application. De la sorte vous pourrez reprendre les concepts étudiés pour vos propres applications. Les langages utilisés seront Java et Python, mais vous pourrez sans aucun problème adapter les motifs à d'autres langages. L'ouvrage du Gang Of Four se basait sur les langages-objet C++ et Small Talk.

II - Les motifs de création

La catégorie des motifs de création regroupe cinq motifs distincts. Dorénavant nous emploierons la désignation anglophone des motifs. Les cinq motifs de ce groupe sont donc les suivants :

- le motif Factory ;

- le motif Abstract Factory ;

- le motif Builder ;

- le motif Prototype ;

- le motif Singleton.

II-A - Application exemple

Notre étude des motifs se limitera à l'apprentissage du motif Factory, ou fabrique. En utilisant le motif de conception Factory nous allons créer une application pouvant lire des données depuis un fichier du disque ou depuis un fichier placé sur un serveur HTTP.

Le principe consiste à créer à la place du programmeur l'instance d'objet la plus adaptée à ses besoins. Deux objets seront utilisés : FileDataReader et URLDataReader. L'un et l'autre héritent de la classe DataReader comme le montre le diagramme ci-dessous.

Héritage des classes

Concevons la classe parente :

public class DataReader
{
protected Reader in = null;

public String read(int bytes) throws IOException
{
if (in == null || bytes <= 0)
throw new IOException("Cannot read from stream");
char[] c = new char[bytes];
int read = in.read(c);
return (read == -1 ? null : new String(c, 0, read));
}
}

Notre classe DataReader possède un champ protégé « in » et une méthode publique. La méthode read() généralise l'utilisation du flux de lecture « in ». Nos implémentations FileDataReader et URLDataReader se contenteront de créer une instance spécifique de « in ». La première doit offrir un flux de lecture de fichier :

public FileDataReader(String path)
{
try
{
in = new BufferedReader(new FileReader(path));
} catch (Exception e) { }
}

La seconde implémentation doit par contre se préoccuper d'ouvrir un flux de communication sur une URL :

public URLDataReader(String url)
{
try
{
URL netURL = new URL(url);
in = new BufferedReader(new InputStreamReader(netURL.openStream()));
} catch (Exception e) { }
}

Dans un cas comme dans l'autre notre seule préoccupation est de fournir une version adéquate de l'objet « in ». Voyons maintenant comment implémenter une fabrique apte à gérer ces implémentations diverses.

II-B - Introduction d'une fabrique

Une fabrique prend toujours pour nom le nom de la super classe du motif auquel on ajoute le suffixe « Factory ». Vous l'avez deviné, notre classe de fabrique se nommera « DataReaderFactory ». Plusieurs méthodes existent pour générer une fabrique :

- la fabrique n'offre que des méthodes statiques qui renvoient les instances souhaitées ;

- la fabrique doit être instanciée par l'opérateur new ;

- la fabrique doit être instanciée par une méthode statique.

Voici des exemples d'utilisation de ces fabriques :

DataReaderFactory.getReader(s);
new DataReaderFactory().getReader(s);
DataReaderFactory.createInstance().getReader(s);

Notre choix se portera sur la première version étant donné la simplicité de notre structure. Toutes les décisions dans le choix des instances à créer s'effectuent donc au sein de la fabrique.

public static DataReader getReader(String path)
{
DataReader reader;
if (path.startsWith("http://"))
reader = new URLDataReader(path);
else
reader = new FileDataReader(path);
return reader;
}

Les critères de choix sont ici simplifiés à l'extrême. Le grand intérêt d'un tel motif de conception réside dans la simplicité d'utilisation des implémentations de nos classes. Le fichier Main.java dans le fichier zip contient une petite application d'exemple capable de lire 512 octets dans un flux. L'utilisateur lance le programme en lui donnant en paramètre un nom de fichier ou une URL. Et nous nous contentons de transmettre ce paramètre à la fabrique sans nous soucier de sa nature.

DataReader r = DataReaderFactory.getReader(args[0]);
System.out.println("Read: " + r.read(512));

Vous pourrez exécuter « java Main ~/.bashrc » aussi bien que « java Main http://www.google.fr ».

Diagramme du motif de conception Factory.

III - Code source

Code source Java à télécharger.

IV - Remerciements

Merci à _MAC_ pour sa relecture orthographique.