web-dev-qa-db-fra.com

Pourquoi le modèle Borg est-il meilleur que le modèle Singleton en Python?

Pourquoi le motif Borg est-il meilleur que le motif Singleton ?

Je demande parce que je ne les vois pas aboutir à quelque chose de différent.

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()

Singleton:

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()

Ce que je veux montrer ici, c'est que l'objet de service, qu'il soit implémenté sous Borg ou Singleton, a un état interne non trivial (il fournit un service basé sur celui-ci) (je veux dire qu'il doit être utile, ce n'est pas un Singleton/Borg juste pour amusement).

Et cet état doit être inité. Ici, l’implémentation de Singleton est plus simple, puisque nous traitons init comme la configuration de l’état global. Je trouve gênant que l'objet Borg doive interroger son état interne pour savoir s'il doit se mettre à jour.

Cela devient pire à mesure que votre état interne augmente. Par exemple, si l'objet doit écouter le signal de suppression de l'application pour enregistrer son registre sur un disque, cet enregistrement ne doit être effectué qu'une seule fois, ce qui est plus facile avec un Singleton.

68
u0b34a0f6ae

La vraie raison pour laquelle borg est différent se résume à un sous-classement.

Si vous sous-classez un borg, les objets de la sous-classe ont le même état que leurs objets de classes parents, à moins que vous ne remplaciez explicitement l'état partagé dans cette sous-classe. Chaque sous-classe du motif singleton a son propre état et produira donc différents objets.

De plus, dans le motif singleton, les objets sont en réalité les mêmes, pas seulement l'état (même si l'état est la seule chose qui compte vraiment).

53
David Raznick

En python, si vous voulez un "objet" unique auquel vous pouvez accéder de n'importe où, créez simplement une classe Unique qui ne contient que des attributs statiques, @staticmethods et @classmethods; vous pourriez l'appeler le modèle unique. Ici, je mets en œuvre et compare les 3 modèles:

Unique

#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

Singleton

#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

Tester

#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))

Sortie:

SINGLETON

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

À mon avis, la mise en œuvre unique est la plus facile, puis Borg et enfin Singleton avec un nombre moche de deux fonctions nécessaires à sa définition.

19
Cristian Garcia

Ce n'est pas. Ce qui n’est généralement pas recommandé est un motif comme celui-ci en python:

class Singleton(object):

 _instance = None

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

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

où vous utilisez une méthode de classe pour obtenir l'instance au lieu du constructeur. La métaprogrammation de Python permet des méthodes bien meilleures, par exemple. celui sur Wikipedia :

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()
13
bayer

Une classe décrit en gros comment vous pouvez accéder (lire/écrire) à l'état interne de votre objet.

Dans le modèle singleton, vous ne pouvez avoir qu'une seule classe, c'est-à-dire que tous vos objets vous donneront les mêmes points d'accès à l'état partagé . Cela signifie que si vous devez fournir une API étendue, vous devrez écrire un wrapper. , enveloppant le singleton

Dans le modèle borg, vous pouvez étendre la classe de base "borg", et donc plus facilement étendre l’API à votre goût.

8
Zed

Ce n'est que mieux dans les quelques cas où vous avez réellement une différence. Comme quand tu sous-classe. Le motif Borg est extrêmement inhabituel, je n’en ai jamais eu besoin depuis 10 ans en programmation Python.

8
Lennart Regebro

En outre, le modèle de type Borg permet aux utilisateurs de la classe de choisir s’ils souhaitent partager l’état ou créer une instance distincte. (que cela soit ou non une bonne idée est un sujet séparé)

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 = ..
0
volodymyr