观察者方法Python设计模式
观察者方法是一种行为设计模式,它允许您定义或创建订阅机制,以向多个对象发送关于他们正在观察的对象发生的任何新事件的通知。主体基本上由多个对象观察。需要监控主题,并且每当主题发生变化时,都会通知观察者有关变化。这种模式定义了对象之间的一对多依赖关系,以便一个对象改变状态,它的所有依赖项都会得到通知和自动更新。
不使用观察者方法的问题
想象一下,您想创建一个具有不同功能的计算器应用程序,例如加法、减法、将数字的基数更改为十六进制、十进制以及许多其他功能。但是您的一位朋友有兴趣将他最喜欢的数字的基数更改为八进制基数,而您仍在开发应用程序。那么,有什么办法可以解决呢?您的朋友是否应该每天检查应用程序以了解状态?但是您不认为这会导致对应用程序进行大量不必要的访问,而这些访问绝对不是必需的。或者您可能会在每次添加新功能并将通知发送给每个用户时考虑这一点。可以吗?有时是的,但不是每次。可能是一些用户被许多他们真的不想要的不必要的通知冒犯了。
使用观察者方法的解决方案
让我们讨论上述问题的解决方案。在这里,对象Subject成为了众人瞩目的焦点。但它也会通知其他对象,这也是我们通常称之为Publisher的原因。所有想要跟踪发布者状态变化的对象都称为订阅者。
Python3
class Subject:
"""Represents what is being observed"""
def __init__(self):
"""create an empty observer list"""
self._observers = []
def notify(self, modifier = None):
"""Alert the observers"""
for observer in self._observers:
if modifier != observer:
observer.update(self)
def attach(self, observer):
"""If the observer is not in the list,
append it into the list"""
if observer not in self._observers:
self._observers.append(observer)
def detach(self, observer):
"""Remove the observer from the observer list"""
try:
self._observers.remove(observer)
except ValueError:
pass
class Data(Subject):
"""monitor the object"""
def __init__(self, name =''):
Subject.__init__(self)
self.name = name
self._data = 0
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
self.notify()
class HexViewer:
"""updates the Hewviewer"""
def update(self, subject):
print('HexViewer: Subject {} has data 0x{:x}'.format(subject.name, subject.data))
class OctalViewer:
"""updates the Octal viewer"""
def update(self, subject):
print('OctalViewer: Subject' + str(subject.name) + 'has data '+str(oct(subject.data)))
class DecimalViewer:
"""updates the Decimal viewer"""
def update(self, subject):
print('DecimalViewer: Subject % s has data % d' % (subject.name, subject.data))
"""main function"""
if __name__ == "__main__":
"""provide the data"""
obj1 = Data('Data 1')
obj2 = Data('Data 2')
view1 = DecimalViewer()
view2 = HexViewer()
view3 = OctalViewer()
obj1.attach(view1)
obj1.attach(view2)
obj1.attach(view3)
obj2.attach(view1)
obj2.attach(view2)
obj2.attach(view3)
obj1.data = 10
obj2.data = 15
类图
下面是观察者方法的类图
输出
DecimalViewer: Subject Data 1 has data 10
HexViewer: Subject Data 1 has data 0xa
OctalViewer: SubjectData 1has data 0o12
DecimalViewer: Subject Data 2 has data 15
HexViewer: Subject Data 2 has data 0xf
OctalViewer: SubjectData 2has data 0o17
好处
- 开放/封闭原则:与其他方法相比,在观察者方法中引入订阅者类要容易得多,而无需更改客户端代码。
- 建立关系:在运行时建立对象之间的关系真的很容易。
- 描述:它仔细描述了对象和观察者之间存在的耦合。因此,无需修改 Subject 来添加或删除观察者。
缺点
- Memory Leakage:由于显式注册和注销观察者而导致的Lapsed Listener 问题导致的内存泄漏。
- 随机通知:所有在场的订阅者都以随机顺序收到通知。
- 有风险的实现:如果没有仔细实现该模式,那么您很有可能最终会得到大量复杂的代码。
适用性
- 多依赖:当多个对象依赖于一个对象的状态时,我们应该使用这种模式,因为它为同一对象提供了一个简洁且经过良好测试的设计。
- 获取通知:它用于社交媒体、RSS 提要、电子邮件订阅,您可以选择关注或订阅并接收最新通知。
- 对象的反射:当我们没有将对象紧密耦合时,一个对象中状态的变化必须反映在另一个对象中。
进一步阅读Java中的观察者方法