¿Por qué el patrón Borg es mejor que el patrón Singleton en Python?

¿Por qué el patrón Borg es mejor que el patrón Singleton ?

Lo pregunto porque no los veo resultando en nada diferente.

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

Semifallo:

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

Lo que quiero mostrar aquí es que el objeto de servicio, ya sea implementado como Borg o Singleton, tiene un estado interno no trivial (proporciona algún servicio basado en él) (quiero decir que tiene que ser algo útil, no es un Singleton / Borg solo para divertido).

Y este estado tiene que ser initado. Aquí, la implementación de Singleton es más sencilla, ya que tratamos a init como la configuración del estado global. Me parece incómodo que el objeto Borg tenga que consultar su estado interno para ver si debería actualizarse.

Se vuelve peor cuanto más interno sea el estado. Por ejemplo, si el objeto tiene que escuchar la señal de desassembly de la aplicación para guardar su registro en el disco, ese registro solo debe hacerse una vez, y esto es más fácil con un Singleton.

La verdadera razón por la que el Borg es diferente se debe a la subclasificación.

Si subclasifica un borg, los objetos de la subclase tienen el mismo estado que los objetos de sus clases primarias, a menos que usted invalide explícitamente el estado compartido en esa subclase. Cada subclase del patrón de singleton tiene su propio estado y, por lo tanto, producirá objetos diferentes.

También en el patrón de singleton los objetos son en realidad iguales, no solo el estado (aunque el estado es lo único que realmente importa).

En Python, si desea un “objeto” único al que pueda acceder desde cualquier lugar, simplemente cree una clase Unique que solo contenga atributos estáticos, @staticmethod s y @classmethod s; Podrías llamarlo el Patrón Único. Aquí implemento y comparo los 3 patrones:

Único

 #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 

Semifallo

 #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 

Prueba

 #SINGLETON print "\nSINGLETON\n" A = Singleton.getInstance() B = Singleton.getInstance() print "At first Bx = {} and Ax = {}".format(Bx,Ax) Ax = 2 print "After Ax = 2" print "Now both Bx = {} and Ax = {}\n".format(Bx,Ax) 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 Bx = {} and Ax = {}".format(Bx,Ax) Ax = 2 print "After Ax = 2" print "Now both Bx = {} and Ax = {}\n".format(Bx,Ax) 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 Bx = {} and Ax = {}".format(Bx,Ax) Ax = 2 print "After Ax = 2" print "Now both Bx = {} and Ax = {}\n".format(Bx,Ax) print "Are A and B the same object? Answer: {}".format(id(A)==id(B)) 

Salida:

SEMIFALLO

 At first Bx = 1 and Ax = 1 After Ax = 2 Now both Bx = 2 and Ax = 2 Are A and B the same object? Answer: True BORG At first Bx = 1 and Ax = 1 After Ax = 2 Now both Bx = 2 and Ax = 2 Are A and B the same object? Answer: False UNIQUE At first Bx = 1 and Ax = 1 After Ax = 2 Now both Bx = 2 and Ax = 2 Are A and B the same object? Answer: True 

En mi opinión, la implementación única es la más fácil, luego Borg y finalmente Singleton con un número feo de dos funciones necesarias para su definición.

No lo es. Lo que generalmente no se recomienda es un patrón como este 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 

donde se utiliza un método de clase para obtener la instancia en lugar del constructor. La metaprogtwigción de Python permite métodos mucho mejores, por ejemplo, el de 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() 

Una clase básicamente describe cómo puede acceder (leer / escribir) el estado interno de su objeto.

En el patrón de singleton solo puede tener una sola clase, es decir, todos sus objetos le darán los mismos puntos de acceso al estado compartido. Esto significa que si tiene que proporcionar una API extendida, tendrá que escribir un envoltorio, envolviendo el singleton

En el patrón de borg puede extender la clase “borg” de base y, por lo tanto, extender el API para su gusto.

Solo es mejor en esos pocos casos donde realmente tienes una diferencia. Como cuando subclases. El patrón Borg es extremadamente inusual, nunca lo he necesitado de verdad en diez años de progtwigción en Python.

Además, el patrón similar a Borg permite a los usuarios de la clase elegir si desean compartir el estado o crear una instancia separada. (Si esto puede ser una buena idea o no, es un tema aparte)

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