Singleton to wzorzec kreacyjny. Zapewnia istnienie co najwyżej jednej instancji danej klasy. Instancja ta musi być dostępna z dowolnego miejsca. Przykład zacznę od zupełnie zwyczajnej klasy, stworzenia 2 jej obiektów i sprawdzenia czy to ten sam obiekt:
class Singleton:
pass
x = Singleton()
y = Singleton()
print(id(x))
print(id(y))
print(x is y)
W efekcie dostałem na konsoli wynik:
2639965680864
2639965681032
False
Id obiektów się różnią, co świadczy o tym że są to różne obiekty. Potwierdza to też sprawdzenie wyrażenia "x is y" w ostatniej linii. Przerobiłem nieco naszą klasę przesłaniając metodę "__new__" i ponowiłem test:
class Singleton:
__instancja=None
def __new__(cls, *args, **kwargs):
if cls.__instancja is None:
cls.__instancja=super().__new__(cls,*args,**kwargs)
return cls.__instancja
a=Singleton()
b=Singleton()
print(id(a))
print(id(b))
print(a is b)
Wynik na konsoli:
2597567231368
2597567231368
True
Tym razem otrzymaliśmy te same id i potwierdzenie że jest to ten sam obiekt. Na czym jednak polegał ten fikołek? Co tu się stało? Metoda "__new__" jest wywoływana w chwili tworzenia obiektu. W naszej klasie mamy prywatne pole "__instancja" które będzie zawierało obiekt klasy Singleton. Obiekt naszej klasy "Singleton" będzie zawierał obiekt klasy "Singleton". Teraz zasada działania kodu wewnątrz metody "__new__" jest taka: jeśli nie mam jeszcze zainicjalizowanego obiektu to go tworzymy, a niezależnie czy właśnie go stworzyliśmy czy był już wcześniej zwracamy go. To jest mechanizm który pilnuje istnienia tylko jednego obiektu klasy Singleton. Znając już mechanizm powinniśmy dotrzeć do prostego wniosku. Jeśli istnieje tylko jeden obiekt klasy Singleton, to w ramach tej klasy mogę stworzyć pole które również będzie przechowywane w jednej instancji. Przeanalizujmy poniższy przykład:
class Singleton:
pole=None
__instancja=None
def __new__(cls, *args, **kwargs):
if cls.__instancja is None:
cls.__instancja=super().__new__(cls,*args,**kwargs)
return cls.__instancja
a=Singleton()
b=Singleton()
a.pole='Zawartość pola'
print(b.pole)
W związku z zastosowaniem wzorca "Singleton" obiekty "a" i "b" to w rzeczywistości ten sam obiekt. Z tego powodu mimo przypisania "zawartość pola" do pola "pole" w obiecie "a", wartość ta jest widoczna również w obiekcie "b". Jest to spowodowane wskazywaniem na ten sam adres w pamięci dla obu obiektów. Zasymulujmy wykorzystanie singletona do utrzymywania tylko jednego połączenia z bazą danych:
class DatabaseConnector:
polaczenie=None
instancja=None
def __new__(cls, *args, **kwargs):
if cls.instancja is None:
print('nawiązywanie połączenia')
cls.polaczenie='połączenie do bazy Oracle'
cls.instancja=super().__new__(cls,*args,**kwargs)
return cls.instancja
a=DatabaseConnector()
b=DatabaseConnector()
print(b.polaczenie)
wynik działania na konsoli:
nawiązywanie połączenia
połączenie do bazy Oracle
W powyższym przykładzie "połączenie" jest zwykłym polem tekstowym, ale nic nie stoi na przeszkodzie byśmy tam umieścili cokolwiek innego. Stworzyłem dwa obiekty. W chwili tworzenia obiektu "a" pole "instancja" było w obiekcie klasy "DatabaseConnector" w związku z czym inicjalizujemy to pole uzupełniając także pole "polaczenie". W chwili tworzenia obiektu "b" pole instancja jest już zapełnione w związku z czym "__new__" oddaje już wcześniej zainicjalizowany obiekt bez jego powtórnego inicjalizowania (czyli również zapełniania pola "polaczenie". Zarówno obiekt "a" jak i "b" referują do tego samego miejsca w przestrzeni pamięci - tak więc "a" i "b" to w rzeczywistości ten sam obiekt.
Komentarze (0)
Brak komentarzy...