Blog JSystems - z miłości do programowania

Szukaj

Wzorzec projektowy - Singleton

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.


 

Przyjdź do nas na szkolenie z języka Python! Mamy szereg szkoleń w ofercie, od podstawowych po aplikacje webowe z użyciem Django, analizę danych, tesowanie, machine learning i wiele innych. Sprawdź dostępne szkolenia Python
Zapisz się do newslettera aby otrzymywać najnowsze świeżynki pojawiające się na blogu! Zapisz się do newslettera