Menu
Coddy logo textTech

Patron Singleton

Fait partie de la section Object Oriented Programming du Journey Python de Coddy — leçon 45 sur 64.

Le patron Singleton garantit qu'une classe n'a qu'une seule instance et fournit un point d'accès global à celle-ci. Cela est utile pour des ressources telles que les connexions de base de données ou les 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._instance

La 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émoire

Voici 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}"

# Le premier accès crée la connexion
db1 = DatabaseConnection()
print(db1.connection)

# Le second accès réutilise la même connexion
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ètres

Sortie :

True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
True

Point 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 doivent avoir qu'une seule copie dans toute votre application, comme les connexions à une base de données, les enregistreurs de logs 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.

challenge icon

Défi

Facile

Dans ce défi, vous allez implémenter le patron de conception Singleton pour une classe de connexion à une base de données.

  • Modifiez databaseconnection.py pour implémenter le patron Singleton en suivant les commentaires TODO
  • Le fichier driver.py contient des scénarios de test complets et ne doit pas être modifié

Essayez vous-même

from databaseconnection import DatabaseConnection

# Gestionnaire complet de cas de test
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()  # Should print "Connected to database at localhost"

elif test_case == "disconnect_message":
    db = DatabaseConnection()
    db.connect()
    db.disconnect()  # Should print "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()  # Will still print "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")
quiz iconTestez-vous

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