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()Verifica 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 di configurazione usando il 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, assicurando che esista una sola istanza. Usalo per le risorse di cui dovrebbe esistere una sola copia in tutta la tua 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 scenari di test approfonditi 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 contiene 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