Modello Singleton
Fa parte della sezione Programmazione orientata agli oggetti del percorso Python di Coddy. Lezione 45 di 64.
Il pattern Singleton garantisce che una classe abbia una sola istanza e fornisce un punto di accesso globale a essa. È utile per risorse come le connessioni al database o le impostazioni di configurazione.
Ecco un'implementazione di base del Singleton:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceIl metodo __new__ controlla la creazione degli oggetti. Verifica se esiste già un'istanza prima di crearne una nuova.
Crea due istanze della classe Singleton:
singleton1 = Singleton()
singleton2 = Singleton()Controlla se entrambe le variabili fanno riferimento allo stesso oggetto:
print(singleton1 is singleton2) # True
print(id(singleton1)) # Stesso indirizzo di memoria
print(id(singleton2)) # Stesso indirizzo di memoriaEcco un esempio più pratico con una connessione al database:
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}"
# Il primo accesso crea la connessione
db1 = DatabaseConnection()
print(db1.connection)
# Il secondo accesso riutilizza la stessa connessione
db2 = DatabaseConnection()
print(db2.connection)
print(db1.query("SELECT * FROM users"))
print(db1 is db2)Crea un gestore della configurazione usando 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 - stesse impostazioniOutput:
True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
TruePunto chiave: il pattern Singleton usa __new__ per controllare la creazione degli oggetti, garantendo che esista una sola istanza. Usalo per risorse di cui dovrebbe esistere una sola copia nell'intera applicazione, come le connessioni al database, i logger o i gestori della configurazione. Ricorda che tutte le variabili che puntano a un Singleton fanno riferimento allo stesso oggetto in memoria.
Sfida
FacileIn questa sfida, implementerai il pattern di progettazione Singleton per una classe di connessione al database.
- Modifica
databaseconnection.pyper implementare il pattern Singleton seguendo i commenti TODO - Il file
driver.pycontiene numerosi scenari di test e non deve essere modificato
Provalo tu
from databaseconnection import DatabaseConnection
# Gestore completo dei casi di test
test_case = input()
if test_case == "identity_check":
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Dovrebbe stampare True
elif test_case == "connect_state":
db = DatabaseConnection()
print(f"Initial connected state: {db.connected}") # Dovrebbe stampare False
db.connect()
print(f"After connect: {db.connected}") # Dovrebbe stampare True
elif test_case == "disconnect_state":
db = DatabaseConnection()
db.connect()
db.disconnect()
print(f"After disconnect: {db.connected}") # Dovrebbe stampare False
elif test_case == "multiple_instances_same_state":
db1 = DatabaseConnection()
db1.connect()
db2 = DatabaseConnection()
print(f"db2 connected state: {db2.connected}") # Dovrebbe stampare True
elif test_case == "host_value":
db = DatabaseConnection()
print(f"Default host: {db.host}") # Dovrebbe stampare "localhost"
db.host = "new-server"
db2 = DatabaseConnection()
print(f"New instance host: {db2.host}") # Dovrebbe stampare "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}") # Dovrebbe stampare True
print(f"Host value: {db1.host}") # Dovrebbe stampare "custom-host"
elif test_case == "connect_message":
db = DatabaseConnection()
db.connect() # Dovrebbe stampare "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # Dovrebbe stampare "Disconnected from database"
elif test_case == "attribute_modification":
db1 = DatabaseConnection()
db1.port = 3306
db2 = DatabaseConnection()
print(f"db2 has port attribute: {hasattr(db2, 'port')}") # Dovrebbe stampare True
print(f"db2 port value: {db2.port}") # Dovrebbe stampare 3306
elif test_case == "reset_connection":
db1 = DatabaseConnection()
db1.connect()
print(f"Connected state: {db1.connected}") # Dovrebbe stampare True
db2 = DatabaseConnection()
print(f"New instance connected state: {db2.connected}") # Dovrebbe stampare True
db2.disconnect()
db3 = DatabaseConnection()
print(f"After disconnect, new instance state: {db3.connected}") # Dovrebbe stampare False
elif test_case == "multiple_connects":
db = DatabaseConnection()
db.connect()
db.connect()
db.connect()
print(f"Connected state after multiple connects: {db.connected}") # Dovrebbe stampare True
db.disconnect()
print(f"Connected state after disconnect: {db.connected}") # Dovrebbe stampare False
elif test_case == "host_change_affects_message":
db = DatabaseConnection()
db.host = "custom-server"
db.connect() # Stamperà comunque "Connected to database at localhost"
# Il metodo connect ha un messaggio predefinito che non usa l'attributo host
print("Message still shows localhost: True")Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti di OOP
File esterniIntroduzione a OOPClassi vs. oggettiIl parametro selfMetodiAttributiMetodo costruttore (__init__)Riepilogo - Calcolatrice semplice4Ereditarietà
Ereditarietà di baseLa funzione super()Sovrascrittura dei metodiEreditarietà multiplaOrdine di risoluzione dei metodiRiepilogo - Gerarchia dei dipendenti7Metodi speciali
Introduzione ai metodi magiciSovraccarico degli operatoriMetodi magici dei contenitoriRiepilogo - Lista personalizzata10Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello ObserverModello Strategy2Decoratori
Introduzione ai decoratoriDecoratore di proprietàDecoratore di metodo staticoDecoratore di metodo di classe5Polimorfismo
Ripasso dell'override dei metodiDuck typingClassi e metodi astrattiProgettazione delle interfacceRiepilogo - Calcolatore di forme8Concetti avanzati di OOP
Composizione vs ereditarietàMixinMetodi statici e di classeDecorator di classeContext manager11Pattern di progettazione - Parte 2
Pattern dei comandiPattern adattatorePattern decoratorePattern metodo templatePattern statoPattern composito3Proprietà delle classi
Variabili di istanza e di classeDecorator delle proprietàAttributi privatiRiepilogo - Gestore di conti bancari6Incapsulamento
Membri pubblici, protetti e privatiModificatori di accessoOccultamento delle informazioniDecorator delle proprietà: avanzatiRipasso - Sistema di registri degli studenti12Progetto: Gestione della biblioteca
Panoramica del progettoClassi Libro e UtenteSistema di prestitoFunzionalità di ricercaInterfaccia amministratoreTest e integrazioneEsercitati da solo: Compilatore Python online