Почему шаблон 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 = ..