Design Pattern Singleton

Description

Le design pattern singleton a pour rôle d'empêcher la création de plusieurs objets par une classe. Pour se faire, si l'objet a déjà été créé, sa classe retournera son instance. C'est l'un des design patterns le plus simple, mais c'est aussi le plus puissant. Il peut être implémenté de 2 façons différentes :

  • Lazy
  • Eager

Mise en situation

🖨️ Vous êtes un employé dans une entreprise, lorsque l'employé lance une impression, on vérifie si une imprimante existe déjà. Dans la vie réelle, vous êtes d'accord que l'on ne crée pas une imprimante pour chaque impression, voilà pourquoi nous allons utiliser le singleton pour faire cela.

Avantages et inconvénients

Avantages Inconvénients
✔️Accès contrôlé ❌Programmation procédurale: dans le cas où l'on utilise ce pattern de manière abusive, on perd toute notion d'orienté objet.
✔️Espace de nom réduit ❌Maintenance difficile, car les dysfonctionnements sont très difficile à retracer. Dans le cas où l'on fait des modifications/suppressions, on a du mal à retracer quel objet l'utilise.
✔️Affinage des méthodes simple ❌Réduction des performances: car il représente un goulot d'étranglement par sa singularité
✔️Contrôle du nombre d'instances

Quand l'utiliser ?

💡 Vous avez des tâches récurrentes : pilotes, mécanisme de cache, ... 💡 Écriture de données dans un fichier : journalisation, travaux d'impressions dans un tampon d'imprimante, ...

Diagramme UML

Ce design pattern peut être utilisé de deux manières :

  • Lazy
  • Eager

Implémentation

Commençons par créer la base de notre design pattern : la classe Printer.

Lazy Printer

 package com.design.singleton;

public class Printer {

    private volatile static Printer printer;
    private int numberOfPage = 0;

    private Printer() {}

    public static synchronized  Printer getInstance() {

        if(printer == null) {
            printer = new Printer();
        }

        return printer;
    }

    public void print(String printable, int numberOfPage) {
        this.numberOfPage += numberOfPage;
        System.out.println("This printer has print : " + numberOfPage);
        System.out.println("To be print:" + printable);
        System.out.println("Number of page:" + numberOfPage);
    }

}

Eager Printer

 package com.design.singleton;

public class Printer {

    private volatile static Printer printer = new Printer();
    private int numberOfPage = 0;

    private Printer() {}

    public static synchronized  Printer getInstance() {
        return printer;
    }

    public void print(String printable, int numberOfPage) {
        this.numberOfPage += numberOfPage;
        System.out.println("This printer has print : " + numberOfPage);
        System.out.println("To be print:" + printable);
        System.out.println("Number of page:" + numberOfPage);
    }

}

Maintenant que notre singleton est implémenté, nous allons définir notre classe employé :

Employee

 package com.design.singleton;

public class Employee {

    private String name;

    public Employee(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Maintenant, nous allons tester tout cela dans notre classe main :

Main

 package com.design.singleton;

public class Main {

    public static void main(String[] args) {

        Employee employee = new Employee("John doe");
        Printer printer = Printer.getInstance();

        System.out.println("Employe : " + employee.getName());
        printer.print("Print this text", 10);

        Printer printer2 = Printer.getInstance();

        System.out.println("Employe : " + employee.getName());
        printer.print("Print this text again", 20);

        printer.print("Print this text again again", 20);

        // Check reference
        System.out.println("Object printer: " + printer);
        System.out.println("Object printer: " + printer2);
    }
}

Resultat :

 Employe : John doe
This printer has print: 10
To be print: Print this text
Number of page: 10
Employe : John doe
This printer has print: 20
To be print: Print this text again
Number of page: 20
This printer has print: 20
To be print: Print this text again again
Number of page: 20
Object printer: com.design.singleton.Printer@1a407d53
Object printer: com.design.singleton.Printer@1a407d53

Process finished with exit code 0

Ici, nous avons donc fini notre implémentation. Ce que nous pouvons remarquer :

  • Peu importe que l'on crée une nouvelle instance, la référence de l'objet reste la même.
  • Nous pouvons facilement mettre des conditions pour charger plusieurs instances, par exemple dire que notre imprimante ne peut être implémentée que X fois.

Developpeur et architecte passionné, qui souhaite partagé son univers et ses découvertes afin de rendre les choses plus simple pour chacun