Tại sao mẫu Borg lại tốt hơn mẫu Singleton trong Python


82

Tại sao mẫu Borg lại tốt hơn mẫu Singleton ?

Tôi hỏi vì tôi không thấy chúng dẫn đến điều gì khác biệt.

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

Những gì tôi muốn hiển thị ở đây là đối tượng dịch vụ, cho dù được triển khai dưới dạng Borg hay Singleton, đều có trạng thái bên trong không tầm thường (nó cung cấp một số dịch vụ dựa trên nó) (ý tôi là nó phải là một cái gì đó hữu ích. Nó không phải là Singleton / Borg chỉ dành cho vui vẻ).

Và trạng thái này phải được hủy bỏ. Ở đây, việc triển khai Singleton đơn giản hơn, vì chúng tôi coi init là thiết lập của trạng thái toàn cục. Tôi thấy thật khó xử khi đối tượng Borg phải truy vấn trạng thái bên trong của nó để xem liệu nó có nên tự cập nhật hay không.

Tình trạng bên trong bạn càng trở nên tồi tệ hơn. Ví dụ: nếu đối tượng phải lắng nghe tín hiệu xé nhỏ của Ứng dụng để lưu đăng ký của nó vào đĩa, thì việc đăng ký đó cũng chỉ nên được thực hiện một lần và điều này dễ dàng hơn với Singleton.


1
Borg mẫu? ^ _ ^ Lần đầu tiên tôi nghe nói về nó là c2.com/cgi/wiki?MonostatePattern
Jeffrey Hantin

9
Monostate? Chúng tôi là Martellis. Chúng tôi nói Borg.
u0b34a0f6ae

Câu trả lời:


66

Lý do thực sự khiến borg khác biệt là do phân loại phụ.

Nếu bạn phân lớp một borg, các đối tượng của lớp con có cùng trạng thái với các đối tượng của lớp cha của chúng, trừ khi bạn ghi đè rõ ràng trạng thái được chia sẻ trong lớp con đó. Mỗi lớp con của mẫu singleton có trạng thái riêng và do đó sẽ tạo ra các đối tượng khác nhau.

Cũng trong mô hình singleton, các đối tượng thực sự giống nhau, không chỉ là trạng thái (mặc dù trạng thái là thứ duy nhất thực sự quan trọng).


1
> Ngoài ra trong mô hình singleton, các đối tượng thực sự giống nhau, không chỉ là trạng thái (mặc dù trạng thái là thứ duy nhất thực sự quan trọng). Tại sao đó là một điều xấu?
agiliq

câu hỏi hay uswaretech, nó là một phần của câu hỏi của tôi ở trên. Điều đó được cho là xấu?
u0b34a0f6ae

2
Tôi không nói đó là một điều xấu. Đó là một quan sát không có ý kiến ​​về sự khác biệt. Xin lỗi vì sự nhầm lẫn. Đôi khi singleton sẽ chính xác hơn, ví dụ như bạn thực hiện bất kỳ kiểm tra nào đối với các đối tượng id theo id (obj), mặc dù điều này là hiếm.
David Raznick

Vậy thì làm thế nào Nonetrong python là một Singleton và không phải là một ví dụ của mẫu Borg?
Chang Zhao

@ChangZhao: bởi vì trong trường hợp Không có chúng ta cần phải có cùng danh tính, không chỉ chia sẻ trạng thái. Chúng tôi x is Nonekiểm tra. Hơn nữa, None là một trường hợp đặc biệt vì chúng ta không thể tạo các lớp con của None.
olivecoder

22

Trong python nếu bạn muốn có một "đối tượng" duy nhất mà bạn có thể truy cập từ bất cứ đâu, chỉ cần tạo một lớp Uniquechỉ chứa các thuộc tính tĩnh, @staticmethods và @classmethods; bạn có thể gọi nó là Mẫu độc nhất. Ở đây tôi thực hiện và so sánh 3 mẫu:

Độc nhất

#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

#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

Kiểm tra

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

Đầu ra:

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

Theo ý kiến ​​của tôi, việc triển khai Unique là dễ nhất, sau đó là Borg và cuối cùng là Singleton với một số xấu hai hàm cần thiết cho định nghĩa của nó.


14

Không phải vậy. Điều thường không được khuyến nghị là một mẫu như thế này trong python:

class Singleton(object):

 _instance = None

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

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

nơi bạn sử dụng một phương thức lớp để lấy thể hiện thay vì phương thức khởi tạo. Lập trình siêu chương trình của Python cho phép các phương pháp tốt hơn nhiều, ví dụ như phương pháp trên 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()

+1 Mẫu Monostate (Borg) kém hơn Singleton (vâng, có thể) vì private a = new Borg (); private b = new Borg (); b.mutate (); và a được thay đổi! Thật là khó hiểu?
Michael Deardeuff

5
Tốt nhất / tệ hơn? Điều đó sẽ phụ thuộc vào usecase của bạn phải không. Tôi có thể nghĩ đến một số trường hợp mà bạn muốn trạng thái được bảo toàn như vậy.
RickyA

5
Đây không phải là vấn đề, @MichaelDeardeuff. Đây là hành vi có chủ đích . Chắc họ giống nhau. Một vấn đề IMHO trong mẫu borg là, nếu bạn thêm một số biến khởi tạo trong phương thức Borg .__ init__, chẳng hạn như self.text = "", sau đó thay đổi đối tượng đó như borg1.text = "blah"và sau đó khởi tạo một đối tượng mới `borg2 = Borg ()" - wham! Tất cả các thuộc tính borg1 đó được khởi tạo trong init . đang whiped nên instantiating là không thể - hoặc tốt hơn: thành viên trong mô hình Borg, bạn có thể không khởi tạo các thuộc tính trong init phương pháp!
nerdoc

Điều này có thể xảy ra trong Singleton vì đã có một bộ ifkiểm tra thì đã có một thể hiện, và nếu có, nó chỉ được trả về mà KHÔNG cần khởi tạo ghi đè!
nerdoc

tương tự có thể được (và nên) thực hiện trong Borg init . if __monostate: returnvà sau đó làm self.foo của bạn = 'bar'
Volodymyr

8

Về cơ bản, một lớp mô tả cách bạn có thể truy cập (đọc / ghi) trạng thái bên trong của đối tượng của bạn.

Trong mô hình singleton, bạn chỉ có thể có một lớp duy nhất, tức là tất cả các đối tượng của bạn sẽ cung cấp cho bạn các điểm truy cập giống nhau đến trạng thái chia sẻ. Điều này có nghĩa là nếu bạn phải cung cấp một API mở rộng, bạn sẽ cần viết một trình bao bọc, bao quanh singleton

Trong mẫu borg, bạn có thể mở rộng lớp "borg" cơ sở và do đó mở rộng API cho sở thích của bạn một cách thuận tiện hơn.


8

Nó chỉ tốt hơn trong một vài trường hợp mà bạn thực sự có sự khác biệt. Giống như khi bạn phân lớp. Mẫu Borg cực kỳ khác thường, tôi chưa bao giờ cần nó thực sự trong mười năm lập trình Python.


2

Ngoài ra, mẫu giống Borg cho phép người dùng của lớp lựa chọn nếu họ muốn chia sẻ trạng thái hoặc tạo một cá thể riêng biệt. (cho dù đây có thể là một ý tưởng tốt hay không là một chủ đề riêng biệt)

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 = ..
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.