Modèles de conception Le modèle Singleton

Dans cet article, vous allez apprendre à implémenter le modèle de conception Singleton, et pourquoi et quand utiliser ce modèle dans votre application. Comme son nom l'indique "Singleton", cette méthode nous permet de créer un et un seul objet d'une classe. 

Voyons ce que nous avons sur Wikipedia à propos de ce modèle de conception:

Le modèle singleton est un modèle de conception qui limite l'instanciation d'une classe à un seul objet. Ceci est utile quand un seul objet est nécessaire pour coordonner les actions à travers le système..

Comme mentionné dans la définition ci-dessus, lorsque nous voulons nous assurer qu'un seul et unique objet doit être créé pour une classe, nous devons implémenter le modèle Singleton pour cette classe..

Vous pourriez vous demander pourquoi nous devrions implémenter une telle classe qui nous permet de n'en créer qu'un seul objet. Je dirais qu'il existe de nombreux cas d'utilisation où nous pouvons appliquer ce modèle de conception. Ceux-ci incluent: classe de configuration, classe de session, classe de base de données, etc..

Je vais prendre l'exemple d'une classe de base de données pour cet article. Nous verrons d’abord quel est le problème si un motif Singleton n’est pas implémenté pour une telle classe. 

Le problème

Imaginez une classe de connexion de base de données très simple qui crée une connexion avec la base de données une fois que nous avons créé un objet de cette classe. 

base de données de classes private $ dbName = null, $ dbHost = null, $ dbPass = null, $ dbUser = null; fonction publique __construct ($ dbDetails = array ()) $ this-> dbName = $ dbDetails ['nom_bdd']; $ this-> dbHost = $ dbDetails ['db_host']; $ this-> dbUser = $ dbDetails ['db_user']; $ this-> dbPass = $ dbDetails ['db_pass']; $ this-> dbh = new PDO ('mysql: host ='. $ this-> dbHost. '; dbname ='. $ this-> dbName, $ this-> dbUser, $ this-> dbPass); 

Dans l'exemple de code ci-dessus, vous pouvez voir qu'il établira une connexion à la base de données chaque fois que vous créez un objet de cette classe. Ainsi, si un développeur a créé un objet de cette classe à plusieurs endroits, imaginez le nombre de connexions (identiques) à la base de données qu'il créera avec le serveur de base de données..

Sans le savoir, le développeur commet des erreurs qui ont un impact considérable sur la vitesse de la base de données et du serveur d'applications. Voyons la même chose en créant un objet différent de cette classe.

$ dbDetails = array ('db_name' => 'designpatterns', 'db_host' => 'localhost', 'db_user' => 'root', 'db_pass' => 'mysqldba'); $ db1 = nouvelle base de données ($ dbDetails); var_dump ($ db1); $ db2 = nouvelle base de données ($ dbDetails); var_dump ($ db2); $ db3 = nouvelle base de données ($ dbDetails); var_dump ($ db3); $ db4 = nouvelle base de données ($ dbDetails); var_dump ($ db4); // Objet de sortie (base de données) [1] private 'dbName' => chaîne 'designpatterns' (longueur = 14) private 'dbHost' => chaîne 'localhost' (longueur = 9) private 'dbPass' => chaîne 'mysqldba' (longueur = 8) private 'dbUser' => chaîne 'root' (longueur = 4) public 'dbh' => objet (PDO) [2] objet (base de données) [3] private 'dbName' => chaîne 'designpatterns' (longueur = 14) privé 'dbHost' => chaîne 'localhost' (longueur = 9) privé 'dbPass' => chaîne 'mysqldba' (longueur = 8) privé 'dbUser' => chaîne 'root' (longueur = 4) public 'dbh' => objet (PDO) [4] objet (base de données) [5] private 'dbName' => chaîne 'designpatterns' (longueur = 14) private 'dbHost' => chaîne 'localhost' (longueur = 9) private 'dbPass' => chaîne 'mysqldba' (longueur = 8) private 'dbUser' => chaîne 'root' (longueur = 4) public 'dbh' => objet (PDO) [6] objet (base de données) [7] private 'dbName' => chaîne 'designpatterns' (longueur = 14) private 'dbHost' => chaîne 'localhost' (longueur = 9) private 'dbPass' => chaîne 'mysqldba' (longueur = 8) private 'dbUser' = > chaîne 'racine' (longueur = 4) publique 'dbh' => o objet (AOP) [8] 

Si vous voyez la sortie du code ci-dessus et la sortie ci-dessus, vous pouvez voir que chaque objet se voit attribuer un nouvel ID de ressource, de sorte que tous les objets constituent une nouvelle référence et allouent par conséquent une mémoire séparée. Si inconsciemment, notre application occupera des ressources qui ne sont vraiment pas nécessaires.

La solution

Ce n'est pas dans notre contrôle comment les développeurs utilisent notre framework de base. Il est sous notre contrôle après le processus de révision du code, mais pendant le développement, nous ne pouvons pas les suivre tout le temps..

Pour surmonter une telle situation, nous devrions créer notre classe de base de manière à ce qu'elle ne puisse pas créer plusieurs objets d'une classe. à la place, il donnera un objet déjà créé, le cas échéant. C'est le cas où nous devrions envisager de développer un modèle Singleton pour nos classes de base..

Lors de la mise en œuvre de ce modèle, notre objectif sera de permettre la création d’un objet d’une classe une seule fois. Permettez-moi d’ajouter le code de classe ci-dessous, puis nous passerons en revue chaque partie de cette classe..

base de données de classes private $ dbName = null, $ dbHost = null, $ dbPass = null, $ dbUser = null; private statique $ instance = null; fonction privée __construct ($ dbDetails = array ()) // Veuillez noter qu'il s'agit d'un constructeur privé $ this-> dbName = $ dbDetails ['db_name']; $ this-> dbHost = $ dbDetails ['db_host']; $ this-> dbUser = $ dbDetails ['db_user']; $ this-> dbPass = $ dbDetails ['db_pass']; // Votre code ici pour vous connecter à la base de données // $ this-> dbh = new PDO ('mysql: host ='. $ This-> dbHost. '; Dbname ='. $ This-> dbName, $ this-> dbUser , $ this-> dbPass);  fonction statique publique connect ($ dbDetails = array ()) // Vérifier si une instance existe déjà if (self :: $ instance == null) self :: $ instance = nouvelle base de données ($ dbDetails);  return self :: $ instance;  fonction privée __clone () // arrêt de la fermeture d'objet fonction privée __wakeup () // arrêt de la non-sérialisation de l'objet

Peu d'indications indiquent que la classe ci-dessus est une classe Singleton. La première chose à faire est un constructeur privé, qui empêche la création d'objets à l'aide du nouveau mot-clé. Une autre indication est une variable membre statique qui contient la référence à un objet déjà créé..

$ dbDetails = array ('db_name' => 'designpatterns', 'db_host' => 'localhost', 'db_user' => 'root', 'db_pass' => 'mysqldba'); $ db1 = database :: connect ($ dbDetails); var_dump ($ db1); $ db2 = database :: connect ($ dbDetails); var_dump ($ db2); $ db3 = database :: connect ($ dbDetails); var_dump ($ db3); $ db4 = database :: connect ($ dbDetails); var_dump ($ db4); // Objet de sortie (base de données) [1] private 'dbName' => chaîne 'designpatterns' (longueur = 14) private 'dbHost' => chaîne 'localhost' (longueur = 9) private 'dbPass' => chaîne 'mysqldba' (longueur = 8) private 'dbUser' => chaîne 'root' (longueur = 4) public 'dbh' => objet (PDO) [2] objet (base de données) [1] private 'dbName' => chaîne 'designpatterns' (longueur = 14) privé 'dbHost' => chaîne 'localhost' (longueur = 9) privé 'dbPass' => chaîne 'mysqldba' (longueur = 8) privé 'dbUser' => chaîne 'root' (longueur = 4) public 'dbh' => objet (PDO) [2] objet (base de données) [1] privé 'dbName' => chaîne 'designpatterns' (longueur = 14) privé 'dbHost' => chaîne 'localhost' (longueur = 9) private 'dbPass' => chaîne 'mysqldba' (longueur = 8) private 'dbUser' => chaîne 'root' (longueur = 4) public 'dbh' => objet (PDO) [2] objet (base de données) [1] private 'dbName' => chaîne 'designpatterns' (longueur = 14) private 'dbHost' => chaîne 'localhost' (longueur = 9) private 'dbPass' => chaîne 'mysqldba' (longueur = 8) private 'dbUser' = > chaîne 'racine' (longueur = 4) publique 'dbh' => o objet (AOP) [2] 

Si vous comparez la sortie des deux sections, vous verrez que, dans la sortie du modèle Singleton, l'ID de ressource pour l'objet est le même pour tous les objets. Mais ce n'est pas le cas lorsque le motif de conception n'est pas utilisé.

Singleton comme anti-modèle

Ce motif est également appelé anti-motif pour diverses raisons, que je mentionnerai ci-dessous:

  1. Il viole le principe de responsabilité unique en raison de sa qualité de contrôle de sa propre création et de son cycle de vie..
  2. Il introduit un état global à votre application. Je dirais que l'état global est très mauvais parce que n'importe quel code peut changer sa valeur. Donc, au moment du débogage, il est très difficile de trouver quelle partie du code a rendu l’étape actuelle de la variable globale.
  3. Singleton est généralement une mauvaise idée si vous effectuez des tests unitaires, et c'est généralement une mauvaise idée de ne pas effectuer de tests unitaires..

Conclusion

J'ai essayé de mon mieux pour expliquer le modèle de conception Singleton, qui est largement discuté sur Internet. J'espère que vous trouverez cet article utile. Nous avons couvert les deux aspects de ce modèle, à savoir un modèle de conception et un modèle anti-modèle..

Veuillez poster vos commentaires, suggestions et / ou questions ci-dessous, et je posterai ma réponse dès que possible. Vous pouvez également me joindre sur Twitter @XpertDevelopers ou m'envoyer immédiatement un email..