Dlaczego wzorzec Borg jest lepszy niż wzorzec Singleton w Pythonie

82

Dlaczego wzór Borga jest lepszy niż wzór Singletona ?

Pytam, ponieważ nie widzę, aby skutkowały czymś innym.

Borg:

class Borg:
  __shared_state = {}
  # init internal state variables here
  __register = {}
  def __init__(self):
    self.__dict__ = self.__shared_state
    if not self.__register:
      self._init_default_register()

Singel:

class Singleton:
  def __init__(self):
    # init internal state variables here
    self.__register = {}
    self._init_default_register()

# singleton mechanics external to class, for example this in the module
Singleton = Singleton()

Chcę tutaj pokazać, że obiekt usługi, niezależnie od tego, czy jest zaimplementowany jako Borg czy Singleton, ma nietrywialny stan wewnętrzny (zapewnia pewną usługę na jego podstawie) (mam na myśli to, że musi to być coś użytecznego, a nie Singleton / Borg tylko dla zabawa).

I ten stan musi zostać zainicjowany. W tym przypadku implementacja Singletona jest prostsza, ponieważ traktujemy init jako konfigurację stanu globalnego. Uważam za niezręczne, że obiekt Borg musi sprawdzać swój stan wewnętrzny, aby sprawdzić, czy powinien się zaktualizować.

Staje się gorzej, im bardziej masz stan wewnętrzny. Na przykład, jeśli obiekt musi nasłuchiwać sygnału rozłączania Aplikacji, aby zapisać swój rejestr na dysku, ta rejestracja również powinna być wykonana tylko raz, a jest to łatwiejsze w przypadku Singletona.

u0b34a0f6ae
źródło
1
Wzór Borga? ^ _ ^ Po raz pierwszy usłyszałem o tym jako c2.com/cgi/wiki?MonostatePattern
Jeffrey Hantin
9
Monostate? Jesteśmy Martellis. Mówimy Borg.
u0b34a0f6ae

Odpowiedzi:

66

Prawdziwy powód, dla którego borg jest inny, sprowadza się do podklasy.

Jeśli utworzysz podklasę borg, obiekty podklasy mają ten sam stan, co obiekty ich klas nadrzędnych, chyba że jawnie nadpiszesz stan współużytkowania w tej podklasie. Każda podklasa wzorca singleton ma swój własny stan i dlatego będzie wytwarzać różne obiekty.

Również we wzorcu singletonowym obiekty są w rzeczywistości takie same, a nie tylko stan (chociaż stan jest jedyną rzeczą, która naprawdę ma znaczenie).

David Raznick
źródło
1
> Również we wzorcu singletonowym obiekty są w rzeczywistości takie same, a nie tylko stan (chociaż stan jest jedyną rzeczą, która naprawdę ma znaczenie). Dlaczego to jest zła rzecz?
agiliq
dobre pytanie uswaretech, to część mojego pytania powyżej. Co to jest powiedziane jako złe?
u0b34a0f6ae
2
Nie powiedziałem, że to zła rzecz. Była to bez opinii obserwacja różnic. Przepraszam za zamieszanie. Czasami singleton będzie lepszy w rzeczywistości, jeśli na przykład sprawdzisz obiekty id przez id (obj), nawet jeśli jest to rzadkie.
David Raznick
Więc jak Nonew Pythonie jest Singleton, a nie przykład wzorca Borg?
Chang Zhao,
@ChangZhao: ponieważ w przypadku None musimy mieć tę samą tożsamość, a nie tylko udostępniać stan. Robimy x is Nonekontrole. Ponadto None jest przypadkiem specjalnym, ponieważ nie możemy tworzyć podklas None.
olivecoder
23

W Pythonie, jeśli chcesz mieć unikalny „obiekt”, do którego masz dostęp z dowolnego miejsca, po prostu utwórz klasę, Uniquektóra zawiera tylko statyczne atrybuty, @staticmethods i @classmethods; można to nazwać Unikalnym Wzorem. Tutaj implementuję i porównuję 3 wzorce:

Wyjątkowy

#Unique Pattern
class Unique:
#Define some static variables here
    x = 1
    @classmethod
    def init(cls):
        #Define any computation performed when assigning to a "new" object
        return cls

Singel

#Singleton Pattern
class Singleton:

    __single = None 

    def __init__(self):
        if not Singleton.__single:
            #Your definitions here
            self.x = 1 
        else:
            raise RuntimeError('A Singleton already exists') 

    @classmethod
    def getInstance(cls):
        if not cls.__single:
            cls.__single = Singleton()
        return cls.__single

Borg

#Borg Pattern
class Borg:

    __monostate = None

    def __init__(self):
        if not Borg.__monostate:
            Borg.__monostate = self.__dict__
            #Your definitions here
            self.x = 1

        else:
            self.__dict__ = Borg.__monostate

Test

#SINGLETON
print "\nSINGLETON\n"
A = Singleton.getInstance()
B = Singleton.getInstance()

print "At first B.x = {} and A.x = {}".format(B.x,A.x)
A.x = 2
print "After A.x = 2"
print "Now both B.x = {} and A.x = {}\n".format(B.x,A.x)
print  "Are A and B the same object? Answer: {}".format(id(A)==id(B))


#BORG
print "\nBORG\n"
A = Borg()
B = Borg()

print "At first B.x = {} and A.x = {}".format(B.x,A.x)
A.x = 2
print "After A.x = 2"
print "Now both B.x = {} and A.x = {}\n".format(B.x,A.x)
print  "Are A and B the same object? Answer: {}".format(id(A)==id(B))


#UNIQUE
print "\nUNIQUE\n"
A = Unique.init()
B = Unique.init()

print "At first B.x = {} and A.x = {}".format(B.x,A.x)
A.x = 2
print "After A.x = 2"
print "Now both B.x = {} and A.x = {}\n".format(B.x,A.x)
print  "Are A and B the same object? Answer: {}".format(id(A)==id(B))

Wynik:

SINGEL

At first B.x = 1 and A.x = 1
After A.x = 2
Now both B.x = 2 and A.x = 2

Are A and B the same object? Answer: True

BORG

At first B.x = 1 and A.x = 1
After A.x = 2
Now both B.x = 2 and A.x = 2

Are A and B the same object? Answer: False

UNIQUE

At first B.x = 1 and A.x = 1
After A.x = 2
Now both B.x = 2 and A.x = 2

Are A and B the same object? Answer: True

Moim zdaniem implementacja Unique jest najłatwiejsza, potem Borg i wreszcie Singleton z brzydką liczbą dwóch funkcji potrzebnych do jej zdefiniowania.

Cristian Garcia
źródło
14

Nie jest. To, co generalnie nie jest zalecane, to taki wzorzec w Pythonie:

class Singleton(object):

 _instance = None

 def __init__(self, ...):
  ...

 @classmethod
 def instance(cls):
  if cls._instance is None:
   cls._instance = cls(...)
  return cls._instance

gdzie używasz metody klasy, aby uzyskać instancję zamiast konstruktora. Metaprogramowanie w Pythonie pozwala na znacznie lepsze metody, np. Tę z Wikipedii :

class Singleton(type):
    def __init__(cls, name, bases, dict):
        super(Singleton, cls).__init__(name, bases, dict)
        cls.instance = None

    def __call__(cls, *args, **kw):
        if cls.instance is None:
            cls.instance = super(Singleton, cls).__call__(*args, **kw)

        return cls.instance

class MyClass(object):
    __metaclass__ = Singleton

print MyClass()
print MyClass()
Bayer
źródło
+1 Wzór Monostate (Borg) jest gorszy niż Singleton (tak, jest to możliwe), ponieważ private a = new Borg (); prywatny b = nowy Borg (); b.mutate (); i jest zmieniony! Jak bardzo to zagmatwane?
Michael Deardeuff,
5
Najlepsze / gorsze? To zależałoby od twojego przypadku, prawda? Przychodzi mi do głowy wiele przypadków, w których chciałbyś, aby stan taki został zachowany.
RickyA
5
To nie jest problem, @MichaelDeardeuff. To jest zamierzone zachowanie . Powinny być takie same. Problem IMHO we wzorcu borg polega na tym, że jeśli dodasz jakieś zmienne inicjalizacyjne w metodzie Borg .__ init__, na przykład self.text = "", to zmień ten obiekt na przykład, borg1.text = "blah"a następnie utwórz instancję nowego obiektu `borg2 = Borg ()" - bum! Wszystkie atrybuty borg1, które są inicjowane w init są biczowane. więc tworzenie instancji jest niemożliwe - lub lepiej: we wzorcu Borg NIE
MOŻESZ
Jest to możliwe w Singletonie, ponieważ istnieje ifsprawdzenie, czy istnieje już instancja, a jeśli tak, jest ona zwracana BEZ nadpisującej inicjalizacji!
nerdoc
to samo można (i powinno się) zrobić w Borg init . if __monostate: returna potem wykonaj self.foo = 'bar'
volodymyr
8

Klasa w zasadzie opisuje, w jaki sposób można uzyskać dostęp (odczyt / zapis) do stanu wewnętrznego obiektu.

We wzorcu singleton możesz mieć tylko jedną klasę, tzn. Wszystkie Twoje obiekty dadzą ci te same punkty dostępu do wspólnego stanu. Oznacza to, że jeśli musisz zapewnić rozszerzone API, będziesz musiał napisać wrapper, zawijający się wokół singletona

We wzorcu borg możesz rozszerzyć podstawową klasę „borg”, a tym samym wygodniej rozszerzyć API według własnego uznania.

Zed
źródło
8

Jest lepiej tylko w tych kilku przypadkach, w których faktycznie masz różnicę. Tak jak w przypadku podklasy. Wzorzec Borg jest niezwykle niezwykły, nigdy nie potrzebowałem go naprawdę przez dziesięć lat programowania w Pythonie.

Lennart Regebro
źródło
2

Ponadto wzorzec podobny do Borga pozwala użytkownikom klasy na wybór, czy chcą współdzielić stan, czy utworzyć oddzielną instancję. (czy to może być dobry pomysł to osobny temat)

class MayBeBorg:
    __monostate = None

    def __init__(self, shared_state=True, ..):
        if shared_state:

            if not MayBeBorg.__monostate:
                MayBeBorg.__monostate = self.__dict__
            else:
                self.__dict__ = MayBeBorg.__monostate
                return
        self.wings = ..
        self.beak = ..
volodymyr
źródło