为什么在Python中Borg模式比Singleton模式更好

91

为什么 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()

单例模式:

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

而且这个状态必须被初始化。在这里,Singleton的实现更为直接,因为我们将init视为全局状态的设置。我觉得Borg对象必须查询其内部状态以查看是否应该更新自己,这让我感到尴尬。

当内部状态越多时,情况会变得更糟。例如,如果对象必须监听应用程序的关闭信号来将其注册表保存到磁盘上,那么只应进行一次注册,使用Singleton更容易实现。


1
Borg模式?^_^ 我第一次听说是在http://c2.com/cgi/wiki?MonostatePattern。 - Jeffrey Hantin
12
单体模式?我们是马泰利家族。我们说博格(Borg)。 - u0b34a0f6ae
这里有一些对Borg的赞誉:https://code.activestate.com/recipes/66531/ - NeilG
6个回答

71

伯格模式(borg)之所以不同的真正原因在于子类化。

如果你对伯格进行子类化,子类的对象将与其父类的对象具有相同的状态,除非你在该子类中明确地覆盖了共享状态。每个单例模式的子类都有自己的状态,因此将生成不同的对象。

而在单例模式中,对象实际上是相同的,而不仅仅是状态(尽管状态是唯一真正重要的部分)。


1
在单例模式中,对象实际上是相同的,不仅仅是状态(尽管状态是唯一真正重要的事情)。这为什么是个坏事? - agiliq
很好的问题,uswaretech,这是我的问题的一部分。那被称为不好吗? - u0b34a0f6ae
2
我并没有说这是一件坏事。这只是对差异的客观观察而已,没有包含个人意见。抱歉造成了混淆。有时候单例模式确实会更好,例如如果你需要逐个检查对象的id(通过id(obj)),尽管这种情况很少发生。 - David Raznick
那么在Python中,None如何成为单例而不是Borg模式的示例呢? - Chang Zhao
1
@ChangZhao:因为在None的情况下,我们需要具有相同的身份,不仅共享状态。我们进行x is None检查。此外,None是一个特殊情况,因为我们无法创建None的子类。 - olivecoder

30

如果你想在Python中创建一个可以从任何地方访问的唯一“对象”,只需创建一个仅包含静态属性、@staticmethod和@classmethod的类Unique,你可以将其称为唯一模式。 这里我实现并比较了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
#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模式,但它需要定义两个函数,显得有些丑陋。

这是所有三者的一个很好的例子。谢谢! - joeyagreco

16

不是。一般不推荐在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()

单例模式(Borg)比单例模式更糟糕(是的,这是可能的),因为私有a = new Borg();私有b = new Borg(); b.mutate();并且a被改变!多么令人困惑啊? - Michael Deardeuff
5
最好/最差的?这取决于您的使用情况。我可以想到很多情况下您会想要保留这样的状态。 - RickyA
6
@MichaelDeardeuff,这不是一个问题。这是一种旨在的行为。它们应该是相同的。在 Borg 模式中我的个人看法是存在一个问题,如果你在 Borg.init 方法中添加一些初始化变量,例如 self.text = "",然后更改该对象,比如 borg1.text = "blah",然后实例化一个新对象 borg2 = Borg() - 突然间!所有在 init 中初始化的 borg1 属性都被清除了。因此实例化是不可能的 - 或者更好地说:在 Borg 模式中,你不应该在 init 方法中初始化成员属性! - nerdoc
这在单例模式中是可能的,因为有一个 if 检查是否已经存在实例,如果是,则只返回它,而不进行覆盖初始化! - nerdoc
在 Borg init 中可以做同样的事情(也应该这样做)。if __monostate: return,然后再执行 self.foo='bar' - volodymyr
当我阅读元编程版本“Singleton()”的用法时,它看起来好像创建了几个实例,不是吗?语法Singleton.instance()似乎更加明确。(可能是因为我来自Java。) - Stefan

8

类基本上描述了您如何访问(读取/写入)对象的内部状态。

在单例模式中,您只能拥有一个类,即所有对象都将为您提供对共享状态的相同访问点。 这意味着,如果您必须提供扩展API,则需要编写一个包装器,围绕单例进行封装。

在博格模式中,您可以扩展基础“博格”类,从而更方便地扩展适合您口味的API。


8

只有在确实存在差异的那几种情况下,它才更好。比如在子类化时。Borg模式非常不寻常,我在十年的Python编程中从未真正需要它。


3
此外,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 = ..

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接