CodeGym/Blog Java/Random-FR/Classe Java Singleton
Auteur
Artem Divertitto
Senior Android Developer at United Tech

Classe Java Singleton

Publié dans le groupe Random-FR
membres
Salut! Aujourd'hui, nous allons plonger dans les détails de divers modèles de conception, en commençant par le modèle Java Singleton. Passons en revue : que savons-nous des modèles de conception en général ? Les modèles de conception sont les meilleures pratiques que nous pouvons appliquer pour résoudre un certain nombre de problèmes connus. Les modèles de conception ne sont généralement liés à aucun langage de programmation. Considérez-les comme un ensemble de recommandations pour vous aider à éviter les erreurs et à éviter de réinventer la roue.Modèles de conception : Singleton - 1

Qu'est-ce qu'un singleton en Java ?

Singleton est l'un des modèles de conception les plus simples au niveau de la classe. Parfois, les gens disent "cette classe est singleton", ce qui signifie que la classe implémente le modèle de conception singleton. Parfois, il est nécessaire d'écrire une classe où nous restreignons l'instanciation à un seul objet. Par exemple, une classe responsable de la journalisation ou de la connexion à un base de données. Le modèle de conception singleton décrit comment nous pouvons y parvenir. Un singleton est un modèle de conception qui fait deux choses :
  1. Il garantit qu'il n'y aura jamais qu'une seule instance de la classe.

  2. Il fournit un point d'accès global unique à cette instance.

Par conséquent, il existe deux caractéristiques caractéristiques de presque toutes les implémentations du modèle singleton :
  1. Un constructeur privé. Cela limite la possibilité de créer des objets de la classe en dehors de la classe elle-même.

  2. Une méthode statique publique qui renvoie l'instance de la classe. Cette méthode s'appelle getInstance . C'est le point d'accès global à l'instance de classe.

Options de mise en œuvre

Le modèle de conception singleton est appliqué de différentes manières. Chaque option est bonne et mauvaise à sa manière. Comme toujours, il n'y a pas d'option parfaite ici, mais nous devrions nous efforcer d'en trouver une. Tout d'abord, décidons de ce qui constitue le bien et le mal, et quelles mesures affectent la façon dont nous évaluons les différentes implémentations du modèle de conception. Commençons par le bon. Voici les facteurs qui rendent une mise en œuvre plus juteuse et attrayante :
  • Initialisation différée : l'instance n'est pas créée tant qu'elle n'est pas nécessaire.

  • Code simple et transparent : cette métrique, bien sûr, est subjective, mais elle est importante.

  • Thread safety : bon fonctionnement dans un environnement multi-thread.

  • Hautes performances dans un environnement multi-thread : peu ou pas de blocage de thread lors du partage d'une ressource.

Maintenant les inconvénients. Nous énumérerons les facteurs qui mettent une implémentation sous un mauvais jour :
  • Pas d'initialisation paresseuse : lorsque la classe est chargée au démarrage de l'application, qu'elle soit nécessaire ou non (paradoxalement, dans le monde informatique il vaut mieux être paresseux)

  • Code complexe et difficile à lire. Cette mesure est également subjective. Si vos yeux commencent à saigner, nous supposerons que la mise en œuvre n'est pas la meilleure.

  • Manque de sécurité des fils. En d'autres termes, "danger de fil". Opération incorrecte dans un environnement multithread.

  • Performances médiocres dans un environnement multi-thread : les threads se bloquent tout le temps ou souvent lors du partage d'une ressource.

Code

Nous sommes maintenant prêts à envisager diverses options de mise en œuvre et à indiquer les avantages et les inconvénients :

Simple

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return INSTANCE;
    }
}
La mise en œuvre la plus simple. Avantages:
  • Code simple et transparent

  • Sécurité des fils

  • Hautes performances dans un environnement multithread

Les inconvénients:
  • Pas d'initialisation paresseuse.
Pour tenter de corriger la lacune précédente, nous obtenons l'implémentation numéro deux :

Initialisation paresseuse

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {}

  public static Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Avantages:
  • Initialisation paresseuse.

Les inconvénients:
  • Non thread-safe

Cette implémentation est intéressante. Nous pouvons initialiser paresseusement, mais nous avons perdu la sécurité des threads. Pas de soucis - nous synchronisons tout dans l'implémentation numéro trois.

Accès synchronisé

public class Singleton {
  private static final Singleton INSTANCE;

  private Singleton() {
  }

  public static synchronized Singleton getInstance() {
    if (INSTANCE == null) {
      INSTANCE = new Singleton();
    }
    return INSTANCE;
  }
}
Avantages:
  • Initialisation paresseuse.

  • Sécurité des fils

Les inconvénients:
  • Mauvaise performance multithread

Excellent! Dans l'implémentation numéro trois, nous rétablissons la sécurité des threads ! Bien sûr, c'est lent... Maintenant, la méthode getInstance est synchronisée, elle ne peut donc être exécutée que par un seul thread à la fois. Plutôt que de synchroniser l'intégralité de la méthode, nous n'avons en fait besoin que de synchroniser la partie de celle-ci qui initialise la nouvelle instance. Mais nous ne pouvons pas simplement utiliser un bloc synchronisé pour envelopper la partie responsable de la création de la nouvelle instance. Cela ne garantirait pas la sécurité des threads. Tout est un peu plus compliqué. Une bonne synchronisation peut être vue ci-dessous :

Verrouillage à double contrôle

public class Singleton {
    private static final Singleton INSTANCE;

  private Singleton() {
  }

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}
Avantages:
  • Initialisation paresseuse.

  • Sécurité des fils

  • Hautes performances dans un environnement multithread

Les inconvénients:
  • Non pris en charge dans les versions antérieures de Java inférieures à 1.5 (l'utilisation du mot-clé volatile est corrigée depuis la version 1.5)

Notez que pour que cette option d'implémentation fonctionne correctement, l'une des deux conditions doit être satisfaite. La variable INSTANCE doit être finale ou volatile . La dernière implémentation dont nous parlerons aujourd'hui est le singleton class holder .

Titulaire de la classe

public class Singleton {

   private Singleton() {
   }

   private static class SingletonHolder {
       public static final Singleton HOLDER_INSTANCE = new Singleton();
   }

   public static Singleton getInstance() {
       return SingletonHolder.HOLDER_INSTANCE;
   }
}
Avantages:
  • Initialisation paresseuse.

  • Sécurité des fils.

  • Hautes performances dans un environnement multithread.

Les inconvénients:
  • Un fonctionnement correct nécessite une garantie que l' objet singleton est initialisé sans erreur. Sinon, le premier appel à la méthode getInstance entraînera une ExceptionInInitializerError , et tous les appels suivants produiront une NoClassDefFoundError .

Cette mise en œuvre est presque parfaite. Il est paresseux, thread-safe et rapide. Mais il a une nuance, comme expliqué dans la liste des inconvénients. Comparaison de diverses implémentations du modèle singleton :
Mise en œuvre Initialisation paresseuse Sécurité des fils Performances multithread Quand utiliser?
Simple - + Rapide Jamais. Ou éventuellement lorsque l'initialisation paresseuse n'est pas importante. Mais ce ne serait jamais mieux.
Initialisation paresseuse + - N'est pas applicable Toujours lorsque le multithreading n'est pas nécessaire
Accès synchronisé + + Lent Jamais. Ou peut-être lorsque les performances multithread n'ont pas d'importance. Mais ce ne serait jamais mieux.
Verrouillage à double contrôle + + Rapide Dans de rares cas où vous devez gérer des exceptions lors de la création du singleton (lorsque le singleton du détenteur de la classe n'est pas applicable)
Titulaire de la classe + + Rapide Chaque fois que le multithreading est nécessaire et qu'il est garanti que l'objet singleton sera créé sans problème.

Avantages et inconvénients du modèle singleton

En général, un singleton fait exactement ce qu'on attend de lui :
  1. Il garantit qu'il n'y aura jamais qu'une seule instance de la classe.

  2. Il fournit un point d'accès global unique à cette instance.

Cependant, ce modèle présente des lacunes :
  1. Un singleton viole le principe de responsabilité unique : en plus de ses fonctions directes, la classe singleton contrôle également le nombre d'instances.

  2. La dépendance d'une classe ordinaire à un singleton n'est pas visible dans le contrat public de la classe.

  3. Les variables globales sont mauvaises. En fin de compte, un singleton se transforme en une variable globale lourde.

  4. La présence d'un singleton réduit la testabilité de l'application dans son ensemble et des classes qui utilisent le singleton en particulier.

Et c'est tout! :) Nous avons exploré la classe Java Singleton avec vous. Maintenant, pour le reste de votre vie, lorsque vous conversez avec vos amis programmeurs, vous pouvez mentionner non seulement à quel point le modèle est bon, mais aussi quelques mots sur ce qui le rend mauvais. Bonne chance pour maîtriser ces nouvelles connaissances.

Lecture complémentaire :

Commentaires
  • Populaires
  • Nouveau
  • Anciennes
Tu dois être connecté(e) pour laisser un commentaire
Cette page ne comporte pas encore de commentaires