2009-08-23 5 views
61

Pourquoi le Borg pattern est-il meilleur que le Singleton pattern?Pourquoi le motif Borg est-il meilleur que le motif Singleton en Python?

Je demande parce que je ne les vois pas ce qui en résulte 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 est que l'objet de service, que ce soit mis en œuvre comme Borg ou Singleton, a un état interne non négligeable (il fournit quelques service basé dessus) (je veux dire que ça doit être quelque chose d'utile ce n'est pas un Singleton/Borg juste pour s'amuser).

Et cet état doit être initialisé. Ici, l'implémentation de Singleton est plus simple, puisque nous traitons init comme la mise en place de l'état global. Je trouve bizarre que l'objet Borg doive interroger son état interne pour voir s'il devrait se mettre à jour.

Plus l'état interne est dégradé, plus la dégradation est importante. Par exemple, si l'objet doit écouter le signal de démontage de l'application pour enregistrer son registre sur le disque, cet enregistrement ne doit être effectué qu'une seule fois, ce qui est plus facile avec un singleton.

+1

Motif de Borg?^_^J'avais d'abord entendu parler de cela comme http://c2.com/cgi/wiki?MonostatePattern –

+8

Monostate? Nous sommes les Martellis. Nous disons Borg. – u0b34a0f6ae

Répondre

46

La vraie raison pour laquelle borg est différent revient à sous-classer.

Si vous sous-classez un borg, les objets de la sous-classe ont le même état que leurs objets classes parents, sauf si vous substituez explicitement l'état partagé dans cette sous-classe. Chaque sous-classe du modèle singleton a son propre état et produira donc des objets différents. Dans le modèle singleton, les objets sont en fait identiques, pas seulement l'état (même si l'état est la seule chose qui compte vraiment).

+1

> Toujours dans le modèle singleton, les objets sont en fait les mêmes, pas seulement l'état (même si l'état est la seule chose qui compte vraiment) Pourquoi est-ce une mauvaise chose? – agiliq

+0

bonne question uswaretech, c'est une partie de ma question ci-dessus. Qu'est-ce que cela dit comme mauvais? – u0b34a0f6ae

+2

Je n'ai pas dit que c'était une mauvaise chose. C'était une observation sans opinion sur les différences. Désolé pour la confusion. Parfois, le singleton sera mieux infact, si par exemple vous faites des vérifications sur les objets id par id (obj), même si c'est rare. –

13

Ce n'est pas le cas. Ce qui est généralement non recommandée est un modèle 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 de bien meilleures méthodes, par ex. 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() 
+0

+1 Le pattern Monostate (Borg) est plus grave que Singleton (oui, c'est possible) car private a = new Borg(); privé b = nouveau Borg(); b.mutate(); et a est changé! Comment est-ce que c'est confus? –

+4

Meilleur/pire? Cela dépend de votre utilisation, n'est-ce pas? Je peux penser à un certain nombre de cas où vous voudriez que l'état soit préservé comme ça. – RickyA

+4

Ce n'est pas un problème, @MichaelDeardeuff. C'est un comportement intentionnel. Ils devraient être les mêmes. Un problème à mon humble avis dans le modèle borg est que, si vous ajoutez des variables d'initialisation dans la méthode Borg .__ init__, comme 'self.text =" "', alors changez cet objet comme 'borg1.text =" blah "', puis instanciez un nouvel objet 'borg2 = Borg()" - wham, tous les attributs borg1 qui sont initialisés dans __init__ sont whiped donc l'instanciation est impossible - ou mieux: Dans le pattern Borg, vous NE pouvez PAS initialiser les attributs membres dans la méthode __init__! – nerdoc

7

Une classe décrit essentiellement la façon dont vous pouvez accéder (lecture/écriture) 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, entourant le singleton

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

7

C'est seulement mieux dans les rares cas où vous avez réellement une différence. Comme quand vous sous-classe. Le modèle Borg est extrêmement inhabituel, je n'en ai jamais eu besoin pendant dix ans de programmation Python.

17

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

uniques

#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 

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

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 le plus facile, puis Borg et enfin Singleton avec un nombre laide de deux fonctions nécessaires à sa définition.

0

De plus, un motif de type Borg permet aux utilisateurs de la classe de choisir s'ils veulent partager l'état ou créer une instance distincte. (si cela peut être une bonne idée est un sujet distinct)

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 = .. 
Questions connexes