Borg (Monostan) vs Singleton

Singleton jest klasycznym i ogólnie bardzo dobrze znanym wzorcem architektonicznym w oprogramowaniu. Został on opisany między innymi w bardzo znanej książce Gang of four. Obecnie bardzo często się słyszy komentarze, że należałoby go chyba bardziej rozważać jako antywzorzec w produkcji oprogramowania niż jako ‘dobrą’ praktykę. Jest to jednak kwestia troszkę bardziej skomplikowana i nie chciałbym jej poruszać w tym wpisie.

Cytujac wikipedie za definicją Singletona:

Singleton to kreacyjny wzorzec projektowy, którego celem jest ograniczenie możliwości tworzenia obiektów danej klasy do jednej instancji oraz zapewnienie globalnego dostępu do stworzonego obiektu.

Przedstawmy więc przykładową implementację bardzo prostego Singletona.

Implementacja jest bardzo prosta i jej sednem jest użycie dodatkowej wewnętrznej klasy. To właśnie instancja tej klasy jest zwracana kiedy programista tworzy nową instancję Singletona. Czyli jest ona formą ‘kontenera’ dla naszych danych.

Spójrzmy teraz na ten kod:

Elementem na który warto zwrócić uwagę jest to że objA i objB są dokładnie tymi samymi obiektami. Ich id jest identyczne.

Przejdźmy teraz do naszego drugiego wzorca które jest o wiele mniej znany - Borg (zwany też monostanem). Głównym celem tego wzorca jest rozwiązanie tego samego problemu który rozwiązuje Singleton, jednak w troszkę inny sposób. Jako ciekawostkę dodam że nazwa pochodzi z serialu Star Treka.

Zacznijmy może od jego przykładowej implementacji:

Kod również nie należy do najbardziej skomplikowanych. Główna różnica w działaniu pomiędzy Monostanem, a Singletonem leży w sposobie przechowywania danych. Tworzenie nowej instancji klasy Borg nie zwraca za każdym razem dokładnie tego samego obiektu, ale nowo zaalokowany obiekt. To trzymania ‘danych’ (a właściwie stanu) został użyty słownik __shared_state który jest przypisywany do __dict (jest to pole które obiekty w pythonie używają do trzymania swoich stanów).

Kluczowa różnica sprowadza się do tego ze Singleton zwraca zawsze ten sam obiekt, a obiekty w Monostanie są różne, ale współdzielą swoje pola.

Kiedy wiec uzywac Borga a kiedy Singletona?

Bardzo często korzystając z singletona chcemy miec poprostu dane które możemy współdzielić między innymi instancjami danej klasy. Jeżeli nie potrzebujemy informacji, czy te instancje są te dokładnie te same (ich wartość id), możemy użyć wtedy rozwiązania monostanu.

A samo używanie monostanu ma jedną dużą zaletę której nie widać tutaj na pierwszy rzut ok. Jest to możliwość dziedziczenia po tej klasie i mimo to nadal posiadanie stanów które są dzielone. Pomiędzy oryginalną klasa monostanu, jak i obiektów które po niej dziedziczą.

Dziedziczenie może się okazać bardzo przydatne kiedy operujemy na danych dostępnych globalnie, bo pozwala nam w uporządkowany sposób zorganizować metody do operowania na tych współdzielonych danych, czy na przykład nałożyć jakąś specyficzną walidacje, albo ograniczyć dostęp do zapisu danych.

Jedyną zaletą singletona jest nad monostanem jest tylko to że obiekty mają dokładnie to samo id. W każdym innym przypadku powinniśmy przemyśleć użycie monostanu, który można traktować jako taka troszkę lepsza wersja klasycznego singletona.