Почему шаблон Borg лучше, чем шаблон Singleton в Python

Почему шаблон Borg лучше, чем шаблон Singleton?

Я спрашиваю, потому что я не вижу в них ничего другого.

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

То, что я хочу показать здесь, состоит в том, что объект службы, будь он реализован как Borg или Singleton, имеет нетривиальное внутреннее состояние (он предоставляет некоторую услугу на его основе) (я имею в виду, что это должно быть чем-то полезным, а не Singleton/Борг просто для удовольствия).

И это состояние должно быть приведено в действие. Здесь реализация Singleton является более простой, поскольку мы рассматриваем init как настройку глобального состояния. Мне неудобно, что объект Borg должен запросить свое внутреннее состояние, чтобы проверить, не следует ли его обновлять.

Это становится хуже, чем больше у вас внутреннего состояния. Например, если объект должен прослушивать сигнал Teardown приложения, чтобы сохранить его регистр на диске, эта регистрация также должна выполняться только один раз, и это проще с помощью Singleton.

Ответы

Ответ 1

Настоящая причина, по которой borg отличается, сводится к подклассу.

Если вы подклассифицируете borg, объекты подкласса имеют такое же состояние, что и объекты классов родителей, если вы явно не переопределите общее состояние в этом подклассе. Каждый подкласс одноэлементного шаблона имеет свое собственное состояние и поэтому будет создавать разные объекты.

Также в одноэлементном шаблоне объекты на самом деле одинаковы, а не только состояние (даже если состояние - это единственное, что действительно имеет значение).

Ответ 2

В python, если вы хотите, чтобы уникальный "объект", к которому вы можете получить доступ из любого места, просто создайте класс Unique, который содержит только статические атрибуты, @staticmethod s и @classmethod s; вы можете назвать его Уникальным Шаблоном. Здесь я реализую и сравниваю 3 шаблона:

Уникальный

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

Тест

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

Вывод:

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

На мой взгляд, уникальная реализация является самой простой, а затем Borg и, наконец, Singleton с уродливым количеством двух функций, необходимых для ее определения.

Ответ 3

Это не так. Обычно не рекомендуется использовать такой шаблон в python:

class Singleton(object):

 _instance = None

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

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

где вы используете метод класса, чтобы получить экземпляр вместо конструктора. Метапрограммирование на Python позволяет гораздо лучше использовать методы, например. на 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()

Ответ 4

Класс в основном описывает способ доступа (чтения/записи) внутреннего состояния вашего объекта.

В шаблоне singleton вы можете иметь только один класс, т.е. все ваши объекты предоставят вам одинаковые точки доступа для общего состояния. Это означает, что если вам нужно предоставить расширенный API, вам нужно будет написать обертку, обернув вокруг singleton

В шаблоне borg вы можете расширить базовый класс "borg" и тем самым более удобно расширить API по своему вкусу.

Ответ 5

Это только лучше в тех немногих случаях, когда у вас действительно есть разница. Как и при подклассе. Образец Borg чрезвычайно необычен, я никогда не нуждался в этом в течение десяти лет программирования Python.

Ответ 6

Кроме того, Borg-подобный шаблон позволяет пользователям выбора класса, если они хотят разделить это состояние или создать отдельный экземпляр. (может ли это быть хорошей идеей, является отдельной темой)

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