Wzorzec Singleton
Część sekcji Programowanie obiektowe ścieżki Python w Coddy. Lekcja 45 z 64.
Wzorzec Singleton gwarantuje, że klasa ma tylko jedną instancję, i zapewnia globalny punkt dostępu do niej. Jest to przydatne w przypadku zasobów takich jak połączenia z bazą danych lub ustawienia konfiguracji.
Oto podstawowa implementacja wzorca Singleton:
class Singleton:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instanceMetoda __new__ kontroluje tworzenie obiektów. Sprawdza, czy instancja już istnieje, zanim utworzy nową.
Utwórz dwie instancje klasy Singleton:
singleton1 = Singleton()
singleton2 = Singleton()Sprawdź, czy obie zmienne odwołują się do tego samego obiektu:
print(singleton1 is singleton2) # True
print(id(singleton1)) # Ten sam adres pamięci
print(id(singleton2)) # Ten sam adres pamięciOto bardziej praktyczny przykład z połączeniem z bazą danych:
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}"
# Pierwszy dostęp tworzy połączenie
db1 = DatabaseConnection()
print(db1.connection)
# Drugi dostęp ponownie wykorzystuje to samo połączenie
db2 = DatabaseConnection()
print(db2.connection)
print(db1.query("SELECT * FROM users"))
print(db1 is db2)Utwórz menedżer konfiguracji, używając wzorca 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 - te same ustawieniaWynik:
True
140234567890123
140234567890123
Creating new database connection
Connected to MySQL database
Connected to MySQL database
Executing: SELECT * FROM users
True
TrueKluczowa informacja: Wzorzec Singleton wykorzystuje __new__ do kontrolowania tworzenia obiektów, zapewniając istnienie tylko jednej instancji. Używaj go w przypadku zasobów, które powinny mieć tylko jedną kopię w całej aplikacji, takich jak połączenia z bazą danych, loggery lub menedżery konfiguracji. Pamiętaj, że wszystkie zmienne wskazujące na Singleton odwołują się do tego samego obiektu w pamięci.
Wyzwanie
ŁatwyW tym wyzwaniu zaimplementujesz wzorzec projektowy Singleton dla klasy połączenia z bazą danych.
- Edytuj plik
databaseconnection.py, aby zaimplementować wzorzec Singleton zgodnie z komentarzami TODO - Plik
driver.pyzawiera rozbudowane scenariusze testowe i nie należy go modyfikować
Spróbuj swoich sił
from databaseconnection import DatabaseConnection
# Kompleksowa obsługa przypadków testowych
test_case = input()
if test_case == "identity_check":
db1 = DatabaseConnection()
db2 = DatabaseConnection()
print(db1 is db2) # Powinno wypisać True
elif test_case == "connect_state":
db = DatabaseConnection()
print(f"Initial connected state: {db.connected}") # Powinno wypisać False
db.connect()
print(f"After connect: {db.connected}") # Powinno wypisać True
elif test_case == "disconnect_state":
db = DatabaseConnection()
db.connect()
db.disconnect()
print(f"After disconnect: {db.connected}") # Powinno wypisać False
elif test_case == "multiple_instances_same_state":
db1 = DatabaseConnection()
db1.connect()
db2 = DatabaseConnection()
print(f"db2 connected state: {db2.connected}") # Powinno wypisać True
elif test_case == "host_value":
db = DatabaseConnection()
print(f"Default host: {db.host}") # Powinno wypisać "localhost"
db.host = "new-server"
db2 = DatabaseConnection()
print(f"New instance host: {db2.host}") # Powinno wypisać "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}") # Powinno wypisać True
print(f"Host value: {db1.host}") # Powinno wypisać "custom-host"
elif test_case == "connect_message":
db = DatabaseConnection()
db.connect() # Powinno wypisać "Connected to database at localhost"
elif test_case == "disconnect_message":
db = DatabaseConnection()
db.connect()
db.disconnect() # Powinno wypisać "Disconnected from database"
elif test_case == "attribute_modification":
db1 = DatabaseConnection()
db1.port = 3306
db2 = DatabaseConnection()
print(f"db2 has port attribute: {hasattr(db2, 'port')}") # Powinno wypisać True
print(f"db2 port value: {db2.port}") # Powinno wypisać 3306
elif test_case == "reset_connection":
db1 = DatabaseConnection()
db1.connect()
print(f"Connected state: {db1.connected}") # Powinno wypisać True
db2 = DatabaseConnection()
print(f"New instance connected state: {db2.connected}") # Powinno wypisać True
db2.disconnect()
db3 = DatabaseConnection()
print(f"After disconnect, new instance state: {db3.connected}") # Powinno wypisać False
elif test_case == "multiple_connects":
db = DatabaseConnection()
db.connect()
db.connect()
db.connect()
print(f"Connected state after multiple connects: {db.connected}") # Powinno wypisać True
db.disconnect()
print(f"Connected state after disconnect: {db.connected}") # Powinno wypisać False
elif test_case == "host_change_affects_message":
db = DatabaseConnection()
db.host = "custom-server"
db.connect() # Nadal wypisze "Connected to database at localhost"
# Metoda connect ma na stałe wpisany komunikat, który nie korzysta z atrybutu host
print("Message still shows localhost: True")Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneWprowadzenie do programowania obiektowegoKlasy a obiektyParametr selfMetodyAtrybutyMetoda konstruktora (__init__)Powtórzenie – prosty kalkulator4Dziedziczenie
Podstawy dziedziczeniaFunkcja super()Nadpisywanie metodDziedziczenie wielokrotneKolejność rozwiązywania metodPodsumowanie – hierarchia pracowników7Metody specjalne
Wprowadzenie do metod magicznychPrzeciążanie operatorówMagiczne metody kontenerówPowtórka – własna lista10Wzorce projektowe, część 1
Wprowadzenie do wzorców projektowychWzorzec SingletonWzorzec FactoryWzorzec ObserverWzorzec Strategy2Dekoratory
Wprowadzenie do dekoratorówDekorator propertyDekorator metody statycznejDekorator metody klasowej5Polimorfizm
Ponowne omówienie nadpisywania metodTypowanie kaczeKlasy i metody abstrakcyjneProjektowanie interfejsówPodsumowanie — kalkulator kształtów8Zaawansowane koncepcje programowania obiektowego
Kompozycja a dziedziczenieMiksyMetody statyczne i klasoweDekoratory klasMenedżery kontekstu11Wzorce projektowe, część 2
Wzorzec PolecenieWzorzec AdapterWzorzec DekoratorWzorzec Metoda szablonowaWzorzec StanWzorzec Kompozyt3Właściwości klas
Zmienne instancji a zmienne klasoweDekoratory właściwościAtrybuty prywatnePowtórzenie — menedżer kont bankowych6Hermetyzacja
Public, Protected, Private — pamięćModyfikatory dostępuUkrywanie informacjiDekoratory właściwości — zaawansowanePodsumowanie — system ewidencji uczniów12Projekt: System zarządzania biblioteką
Przegląd projektuKlasy Book i UserSystem wypożyczeńFunkcja wyszukiwaniaInterfejs administratoraTestowanie i integracjaPoćwicz samodzielnie: Kompilator Python online