Patron Singleton
Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 45 sur 64.
Le patron de conception Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. Ceci est utile pour des ressources telles que des connexions à une base de données ou des paramètres de configuration.
Voici une implémentation de base du Singleton :
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceLa méthode __new__ contrôle la création d'objets. Elle vérifie si une instance existe déjà avant d'en créer une nouvelle.
Créez deux instances de la classe Singleton :
singleton1 = Singleton()
singleton2 = Singleton()Vérifiez si les deux variables font référence au même objet :
print(singleton1 is singleton2) # True
print(id(singleton1)) # Même adresse mémoire
print(id(singleton2)) # Même adresse mémoireVoici un exemple plus pratique avec une connexion à une base de données :
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
print("Creating new database connection")
return cls._instance
def query(self, sql):
return f"Executing: {sql}"
# First access creates the connection
db1 = DatabaseConnection()
print(db1.connection)
# Second access reuses the same connection
db2 = DatabaseConnection()
print(db2.connection)
print(db1.query("SELECT * FROM users"))
print(db1 is db2)Créez un gestionnaire de configuration en utilisant le Singleton :
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.settings = {}
return cls._instance
def set_setting(self, key, value):
self.settings[key] = value
def get_setting(self, key):
return self.settings.get(key)
config1 = Config()
config1.set_setting("debug", True)
config2 = Config()
print(config2.get_setting("debug")) # True - mêmes paramètresRésultat :
True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
TruePoint clé : Le pattern Singleton utilise __new__ pour contrôler la création d'objets, garantissant qu'une seule instance existe. Utilisez-le pour les ressources qui ne devraient avoir qu'une seule copie dans toute votre application, comme les connexions aux bases de données, les loggers ou les gestionnaires de configuration. N'oubliez pas que toutes les variables pointant vers un Singleton font référence au même objet en mémoire.
Défi
FacileDans ce défi, vous implémenterez le patron de conception Singleton pour une classe de connexion à une base de données.
- Modifiez
databaseconnection.pypour implémenter le patron Singleton en suivant les commentaires TODO - Le fichier
driver.pycontient des scénarios de test approfondis et ne doit pas être modifié
Aide-mémoire
Le patron de conception Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci.
Implémentation de base du Singleton utilisant __new__ :
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceLa méthode __new__ contrôle la création d'objets en vérifiant si une instance existe déjà avant d'en créer une nouvelle.
Test des instances Singleton :
singleton1 = Singleton()
singleton2 = Singleton()
print(singleton1 is singleton2) # True - même objetExemple pratique avec une connexion à une base de données :
class DatabaseConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.connection = "Connected to MySQL database"
return cls._instance
def query(self, sql):
return f"Executing: {sql}"Exemple de gestionnaire de configuration :
class Config:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance.settings = {}
return cls._instance
def set_setting(self, key, value):
self.settings[key] = value
def get_setting(self, key):
return self.settings.get(key)Utilisez le Singleton pour les ressources qui ne devraient avoir qu'une seule copie dans toute votre application, comme les connexions aux bases de données, les loggers ou les gestionnaires de configuration.
Essayez vous-même
from databaseconnection import DatabaseConnection
# Gestionnaire de cas de test complet
test_case = input()
if test_case == "identity_check":
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Devrait afficher True
elif test_case == "connect_state":
db = DatabaseConnection()
print(f"Initial connected state: {db.connected}") # Devrait afficher False
db.connect()
print(f"After connect: {db.connected}") # Devrait afficher True
elif test_case == "disconnect_state":
db = DatabaseConnection()
db.connect()
db.disconnect()
print(f"After disconnect: {db.connected}") # Devrait afficher False
elif test_case == "multiple_instances_same_state":
db1 = DatabaseConnection()
db1.connect()
db2 = DatabaseConnection()
print(f"db2 connected state: {db2.connected}") # Devrait afficher True
elif test_case == "host_value":
db = DatabaseConnection()
print(f"Default host: {db.host}") # Devrait afficher "localhost"
db.host = "new-server"
db2 = DatabaseConnection()
print(f"New instance host: {db2.host}") # Devrait afficher "new-server"
elif test_case == "init_once":
db1 = DatabaseConnection()
db1.host = "custom-host"
db2 = DatabaseConnection()
print(f"Both instances have same host: {db1.host == db2.host}") # Devrait afficher True
print(f"Host value: {db1.host}") # Devrait afficher "custom-host"
elif test_case == "connect_message":
db = DatabaseConnection()
db.connect() # Devrait afficher "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # Devrait afficher "Disconnected from database"
elif test_case == "attribute_modification":
db1 = DatabaseConnection()
db1.port = 3306
db2 = DatabaseConnection()
print(f"db2 has port attribute: {hasattr(db2, 'port')}") # Devrait afficher True
print(f"db2 port value: {db2.port}") # Devrait afficher 3306
elif test_case == "reset_connection":
db1 = DatabaseConnection()
db1.connect()
print(f"Connected state: {db1.connected}") # Devrait afficher True
db2 = DatabaseConnection()
print(f"New instance connected state: {db2.connected}") # Devrait afficher True
db2.disconnect()
db3 = DatabaseConnection()
print(f"After disconnect, new instance state: {db3.connected}") # Devrait afficher False
elif test_case == "multiple_connects":
db = DatabaseConnection()
db.connect()
db.connect()
db.connect()
print(f"Connected state after multiple connects: {db.connected}") # Devrait afficher True
db.disconnect()
print(f"Connected state after disconnect: {db.connected}") # Devrait afficher False
elif test_case == "host_change_affects_message":
db = DatabaseConnection()
db.host = "custom-server"
db.connect() # Affichera toujours "Connected to database at localhost"
# La méthode connect a un message codé en dur qui n'utilise pas l'attribut host
print("Message still shows localhost: True")Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Object Oriented Programming
1Les fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe paramètre selfMéthodesAttributsMéthode constructeur (__init__)Récapitulatif - Calculatrice simple4Héritage
Héritage de baseLa fonction super()Redéfinition de méthodeHéritage multipleOrdre de résolution des méthodesRécapitulatif - Hiérarchie des employés7Méthodes spéciales
Introduction aux méthodes magiquesSurcharge d'opérateursMéthodes magiques de conteneurRécapitulatif - Liste personnalisée10Patrons de conception, Partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FactoryPatron ObserverPatron Strategy2Décorateurs
Introduction aux décorateursDécorateur de propriétéDécorateur de méthode statiqueDécorateur de méthode de classe5Polymorphisme
Retour sur la redéfinition de méthodeDuck TypingClasses et méthodes abstraitesConception d'interfacesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageMixinsMéthodes statiques et de classeDécorateurs de classeGestionnaires de contexte11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Propriétés de classe
Variables d'instance vs variables de classeDécorateurs de propriétéAttributs privésRécapitulatif - Gestionnaire de compte bancaire