I. Introduction

Un design pattern décrit une solution standard, utilisable dans la conception de logiciels, à des questions classiques et récurrentes.

Les patrons de conception les plus connus sont au nombre de vingt-trois : Abstract factory, Builder, Factory, Prototype, Singleton, Adapter, Bridge, Composite, Decorator, Facade, Flyweight, Proxy, Memento, Strategy, Command, Chain of responsability, Interpreter, Iterator, Mediator, Template, Visitor, State et Observer. On les classe généralement en trois familles : Création, Structure et Comportement. Ils sont désignés sous le nom de « Gang of Four » (GOF) en référence aux quatre créateurs du concept.

Cet article vous redonne les points clés pour bien comprendre/utiliser les patterns les plus utiles. Il vous propose surtout de télécharger des mémentos à imprimer vous-même au bureau.

Le mémento vous est offert sous licence « Creative Commons CC BY-NC-SA 3.0 FR ». Vous êtes libre de partager (reproduire, distribuer et communiquer) cette œuvre. Ce n'est pas de la publicité. C'est un cadeau pour lequel je ne demande aucune contrepartie. Toutefois, si vous appréciez ce mémento et si vous l'utilisez/distribuez, ça nous fera plaisir de le savoir. Consultez les FAQ pour en savoir plus.

I-A. Patterns par ordre alphabétique

Voici tous les patterns de cet article, classés par ordre alphabétique :

I-B. Patterns par famille

Patterns de création :

Patterns de structure :

  • bientôt

Patterns de comportement :

  • bientôt

I-C. Mises à jour

La première version de ce document a été écrite avec un seul pattern : le singleton. D'autres patterns (et les mémentos associés) seront ajoutés au fur et à mesure.

29 janvier 2014 : création du document (avec seulement le Singleton)

II. Design patterns

 

II-A. Singleton

II-A-1. Principe

Le singleton permet de s'assurer qu'il n'existe qu'une seule instance d'une classe dans un environnement et pour une durée d'exécution donnés.

Points clés :

  • instance privée et statique ;
  • constructeur privé ;
  • méthode d'accès publique et statique.
Image non disponible
Diagramme de classe du Singleton
Version classique
Sélectionnez

public class DogService {
    private static DogService instance;

    private DogService() {
        ...
    }

    public static DogService getInstance() {
        if (instance == null) {
            instance = new DogService();
        }
        return instance;
    }

    public void truc() {
        ...
    }

    ...
}
Utilisation
Sélectionnez

DogService dogService = DogService.getInstance();

dogService.truc();

II-A-2. Synchronisation

Dans un contexte multithread, l'utilisation du pattern Singleton nécessite des précautions pour limiter les problèmes d'accès concurrents. Il faut synchroniser les appels et les initialisations.

Synchro simple
Sélectionnez

public static synchronized DogService getInstance() {
    if (instance == null) {
        instance = new DogService();
    }
    return instance;
}

Malheureusement, on paie le coût de la synchronisation, réalisée grâce au mot-clé « synchronized », à chaque appel et non pas seulement au premier (i.e. initialisation).

Des variantes (plus ou moins efficaces) permettent d'assurer la synchronisation ; vous en retrouverez une sélection ci-dessous. Je vous recommande également/vivement de consulter l'article de Christophe Jollivet intitulé « Le Singleton en environnement Multithread » dont je me suis allègrement inspiré pour écrire cet article.

II-A-2-a. Double-checked locking

Double-checked locking
Sélectionnez

public static DogService getInstance() {
    if (instance == null) {
        synchronized (DogService.class) {
            if (instance == null) {
                instance = new DogService();
            }
        }
    }
    return instance;
}

La théorie semble parfaite et de nombreux sites Web recommandent cette solution. Le DCL n'apporte pourtant aucune garantie que cela fonctionnera. Ceci n'est pas inhérent à un bogue de la JVM mais au modèle de mémoire qui autorise l'écriture dans le désordre (« out-of-order writes »).

II-A-2-b. Mot-clé volatile

Mot-clé volatile
Sélectionnez

private static volatile DogService instance;

Là encore, c'est une « fausse bonne idée ». Dans les anciennes JVM, ça n'offre pas de garantie. Cela fonctionne dans les nouvelles versions, mais provoque un flush complet du registre du processeur. Le gain de performance recherché en n'utilisant pas la synchronisation est alors nul.

II-A-2-c. Thread local

Thread local
Sélectionnez

public class DogService {
    private static final ThreadLocal tl = new ThreadLocal();
    private static DogService instance;

    private DogService() { 
        ...
    }

    public static DogService getInstance() {
        if (tl.get() == null) {
            synchronized (DogService.class) {
                if (instance == null) {
                    instance = new DogService();
                }
                tl.set(Boolean.TRUE);
            }
        }
        return instance;
    }
}

Défaut : l'utilisation du thread local est un peu lente.

II-A-2-d. Initialisation statique

Initialisation statique
Sélectionnez

private static final DogService instance = new DogService();

L'initialisation est faite au lancement, même si le service n'est finalement jamais utilisé.

II-A-2-e. Inner class

Inner class
Sélectionnez

public class DogService {
    private static class InstanceHolder {
        public static final DogService instance    = new DogService();
    }

    private DogService() { 
        ... 
    }

    public static DogService getInstance() {
        return InstanceHolder.instance;
    }
}

Le chargement des classes est « thread-safe » ; la classe encapsulée n'est initialisée que lors de l'appel de la méthode.

II-A-2-f. Enum

Enum
Sélectionnez

public enum DogService {
    INSTANCE;

    public static DogService getInstance() {
        return INSTANCE;
    }
}
Utilisation
Sélectionnez

final DogService service = DogService.getInstance();

// ou comme un enum
final DogService service = DogService.INSTANCE;

L'emploi d'un enum permet de prendre en compte le cas de la sérialisation facilement et empêche complètement l'initialisation par réflexion.

II-A-3. JSR330

Des frameworks d'injection (CDI, Spring, Guice, etc.) savent initialiser une variable comme un singleton.

@Singleton
Sélectionnez

@Singleton
public class DogService {
    ...
}
Injection
Sélectionnez

public class UneClasse {
    @Inject
    private DogService service;
        ...
    }

    ...

Dans une application professionnelle, il est fort probable que vous utiliserez une bibliothèque comme Spring dont le Singleton est le « scope » par défaut. Avec ce type de framework, on n'a pas besoin (et on ne doit pas) programmer soi-même le Singleton.

II-A-4. Mémento

Téléchargez gratuitement le fichier « memento_singleton.pdf » (461 Ko) pour l'imprimer au bureau.

Image non disponible
Image non disponible
Mémento sur le Singleton face 1
Image non disponible
Mémento sur le Singleton face 2

III. Tous les mémentos

Image non disponible
memento_singleton.pdf
   
     

IV. Conclusion

Et voilà... Revenez de temps en temps pour découvrir les nouveaux mémentos qui seront ajoutés.

Vos retours nous aident à améliorer nos publications. N'hésitez donc pas à commenter cet article sur le forum : 9 commentaires Donner une note à l'article (5)

V. Remerciements

J'adresse de grands remerciements aux personnes qui ont contribué à l'écriture de cet article ainsi qu'aux mémentos associés, en particulier à Mickael BARON, Julien, Logan, Michel Dirix, Nicolas et Philippe DUVAL.

VI. Annexes

VI-A. Liens

Retrouvez quelques design pattern sur Developpez.com :
http://smeric.developpez.com/java/uml/

Article de Christophe Jollivet, intitulé « Le Singleton en environnement Multithread » :
http://christophej.developpez.com/tutoriel/java/singleton/multithread/

VI-B. Liens perso

VI-C. FAQ

VI-C-1. Comment imprimer les mémentos au bureau ?

Les mémentos doivent être imprimés sur du papier au format A4 avec une orientation paysage. Cela est déjà configuré dans les fichiers PDF, mais je vous conseille de vérifier que le pilote de votre imprimante le respecte.

La plupart des imprimantes vont essayer de mettre le document à l'échelle, car les fichiers PDF des mémentos sont conçus pour rogner les marges au maximum. Dans la fenêtre des options d'impression, il faudra donc désactiver la mise à l'échelle. En général, il suffit de cocher (ou de décocher selon que c'est une négation ou une option) la mise à l'échelle.

Image non disponible

Il faudra aussi demander une impression recto verso sur bord court. Le réglage par défaut est généralement d'imprimer sur bord long, ce qui convient aux documents à orientation en portrait, mais pas aux mémentos, qui sont orientés en paysage.

Image non disponible

VI-C-2. Comment plier les mémentos imprimés ?

Les mémentos sont constitués de trois volets qu'il va falloir plier. Il existe deux façons de plier un document constitué de trois volets : en zigzag ou en enroulé.

Image non disponible
pliage en zigzag ou enroulé

Pour les mémentos offerts sur cette page, il convient de faire un pliage enroulé (ou fermé). Une fois plié, le volet 3 se retrouve à l'intérieur.

Image non disponible

Il y a une petite ligne en pointillé en bas du verso.

Image non disponible
Ligne pointillée

Il faut replier le premier volet du mémento sur cette ligne, ce qui créera le premier pli (entre les volets 1 et 2).

Si vous être droitier, je vous conseille de tenir le mémento à l'envers, de sorte que la petite ligne en pointillé soit en haut. Pour pourrez ainsi utiliser votre main droite pour replier le premier volet. Remettez ensuite le document à l'endroit pour faire le second pli.

Image non disponible

Pour obtenir le second pli (entre les volets 2 et 3), il suffit de replier le morceau restant à l'intérieur en se servant du premier pli comme butée. Le second pli devrait alors se faire au niveau de la petite ligne en pointillé.

Image non disponible

VI-C-3. Que permet la licence CC BY-NC-SA 3.0 FR ?

La licence Creative Commons vous demande de respecter trois points : attribution, pas d'utilisation commerciale et pas d'œuvre dérivée. Si vous souhaitez modifier ce mémento, par exemple pour l'utiliser dans une de vos plaquettes ou pour ajouter votre logo, il vous suffit de me contacter et j'aurai le plaisir de vous accompagner et de vous offrir (gratuitement) une version utilisable et plus pratique.

VI-C-4. Peut-on commander des mémentos déjà imprimés et pliés ?

Oui c'est possible. Notre objectif n'est pas de gagner de l'argent grâce aux mémentos. Nous les rédigeons, car notre passion est le développement et que nous souhaitons la partager et la rendre simple. Nous pouvons imprimer les mémentos sur du papier épais, les plier et vous les envoyer. Dans ce cas, nous vous demanderons juste une petite participation aux frais d'envoi.

VI-C-5. Peut-on redistribuer les mémentos ?

La réponse la plus simple serait de dire « oui ». Toutefois, au lieu de distribuer vous-même les mémentos, je vous invite à donner l'adresse de ce document. Les visiteurs pourront ainsi profiter des prochaines mises à jour.

VI-C-6. Comment peut-on vous soutenir ?

Le mieux est de parler de nous en donnant l'adresse de cette page à vos contacts. Nous avons également toujours besoin de sponsors qui nous permettraient, par exemple, de consacrer plus de temps à la création et à l'amélioration de contenus de qualité.

Nous avons également besoin de vos retours. Ils nous aident à améliorer nos publications. Nous sommes preneurs des critiques positives et négatives, des remarques et des bonnes idées.