Чому шаблон Borg кращий за шаблон Singleton у Python


82

Чому шаблон Борга кращий за шаблон Сінглтона ?

Я запитую, бо я не бачу, щоб вони спричинили щось інше.

Борг:

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

Сінглтон:

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 / Borg лише для веселощі).

І цю державу треба запровадити. Тут реалізація Сінглтона є більш простою, оскільки ми розглядаємо init як установку глобальної держави. Мені незручно, що об'єкт Borg повинен запитувати свій внутрішній стан, щоб перевірити, чи слід йому оновлюватись.

Що гірше, чим більше у вас внутрішнього стану. Наприклад, якщо об'єкт повинен прослухати сигнал відключення Програми, щоб зберегти свій регістр на диск, ця реєстрація повинна бути зроблена також лише один раз, і це простіше за допомогою Singleton.


1
Шаблон Борга? ^ _ ^ Я вперше чув про це як c2.com/cgi/wiki?MonostatePattern
Джеффрі Хантін,

9
Монодержава? Ми - Мартелліси. Ми кажемо Борг.
u0b34a0f6ae

Відповіді:


66

Справжня причина того, що борг відрізняється, зводиться до підкласифікації.

Якщо ви підкласуєте borg, об'єкти підкласу мають той самий стан, що і їхні об'єкти батьківських класів, якщо ви явно не перевизначаєте спільний стан у цьому підкласі. Кожен підклас одиночного візерунка має власний стан і, отже, буде створювати різні об'єкти.

Крім того, в одиночному шаблоні об'єкти насправді однакові, а не тільки стан (хоча держава є єдиним, що насправді має значення).


1
> Крім того, в одинарному візерунку об'єкти насправді однакові, а не тільки стан (хоча стан є єдиним, що насправді має значення). Чому це погано?
agiliq

гарне запитання uswaretech, це частина мого запитання вище. Що це сказано як погане?
u0b34a0f6ae

2
Я не сказав, що це погано. Це було спостереження без роздумів щодо відмінностей. Вибачте за непорозуміння. Іноді синглтон буде кращим, якщо, наприклад, ви виконуєте перевірку об'єктів id за ідентифікатором (obj), хоча це трапляється рідко.
Девід Разнік

Тоді як Noneу python є Singleton, а не приклад шаблону Borg?
Чанг Чжао,

@ChangZhao: оскільки у випадку з None нам потрібно мати однакову ідентичність, а не лише спільний стан. Ми робимо x is Noneперевірки. Крім того, None є особливим випадком, оскільки ми не можемо створити підкласи None.
olivecoder

23

У python, якщо вам потрібен унікальний "об'єкт", до якого ви можете отримати доступ з будь-якого місця, просто створіть клас, Uniqueякий містить лише статичні атрибути, @staticmethods та @classmethods; Ви можете назвати це унікальним візерунком. Тут я реалізую та порівняю 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))

Вихід:

ОДИНОЧНИЙ РОЗРЯД

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

На мій погляд, унікальна реалізація найпростіша, тоді це Борг і, нарешті, Сінглтон з потворною кількістю двох функцій, необхідних для її визначення.


14

Це не. Загалом не рекомендується використовувати такий шаблон у 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 дозволяє набагато кращі методи, наприклад, у Вікіпедії :

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

+1 Моностат (Борг) гірший за Сінглтон (так, це можливо), оскільки приватний a = новий Борг (); приватний b = новий Borg (); b.mutate (); і a змінюється! Наскільки це бентежить?
Michael Deardeuff

5
Краще / гірше? Це залежатиме від вашого випадку використання, чи не так. Я можу придумати низку випадків, коли ви хотіли б, щоб держава зберігалася так.
RickyA,

5
Це не проблема, @MichaelDeardeuff. Це передбачувана поведінка . Вони повинні бути однаковими. Проблема IMHO у шаблоні borg полягає в тому, що якщо ви додасте деякі змінні ініціалізації в метод Borg .__ init__, наприклад self.text = "", потім зміните цей об'єкт на зразок, borg1.text = "blah"а потім створите новий об'єкт `borg2 = Borg ()" - wham! All borg1 attributes that ініціалізуються в init , збиваються. тому інстанціювання неможливе - або краще: у шаблоні Borg ви МОЖЕТЕ НЕ ініціалізувати атрибути членів у методі init !
nerdoc

Це можливо в Singleton, оскільки існує ifперевірка, чи вже є екземпляр, і якщо так, він просто повертається БЕЗ переважної ініціалізації!
nerdoc

те саме можна зробити (і потрібно зробити) в Borg init . if __monostate: returnі після цього зробити свій self.foo = «бар»
владимир

8

Клас в основному описує, як ви можете отримати доступ (читати / писати) до внутрішнього стану вашого об'єкта.

У шаблоні singleton ви можете мати лише один клас, тобто всі ваші об'єкти надаватимуть вам однакові точки доступу до загального стану. Це означає, що якщо вам потрібно надати розширений API, вам потрібно буде написати обгортку, обертаючись навколо сингтона

У шаблоні борг ви можете розширити базовий клас "борг" і, таким чином, зручніше розширити API на ваш смак.


8

Це краще лише в тих кількох випадках, коли у вас насправді є різниця. Як коли ви підклас. Шаблон Борга надзвичайно незвичний, за десять років програмування на Python він мені ніколи не був потрібен.


2

Крім того, подібний до 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 = ..
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.