有时,我们希望在一个对象的状态改变时更新另外一组对象。在MVC模式中有这样一个非常常见的例子,假设在两个视图(例如,一个饼图和一个电子表格)中使用同一个模型的数据,无论何时更改了模型,都需要更新两个视图。这就是观察者设计模式要处理的问题。
观察者模式描述单个对象(发布者,又称为主持者或可观察者)与一个或多个对象(订阅者,又称为观察者)之间的发布—订阅关系。在MVC例子中,发布者是模型,订阅者是视图。然而,MVC并非是仅有的发布—订阅例子。信息聚合订阅(比如,RSS或Atom)是另一种例子。许多读者通常会使用一个信息聚合阅读器订阅信息流,每当增加一条新信息时,他们就能自动地获取到更新。
观察者模式背后的思想等同于MVC和关注点分离原则背后的思想,即降低发布者与订阅者之间的耦合度,从而易于在运行时添加/删除订阅者。此外,发布者不关心它的订阅者是谁。它只是将通知发送给所有订阅者。
软件的例子
django-observer源代码包是一个第三方Django包,可用于注册回调函数,之后在某些Django模型字段发生变化时执行。它支持许多不同类型的模型字段(CharField、IntegerField等)。
RabbitMQ可用于为应用添加异步消息支持,支持多种消息协议(比如,HTTP和AMQP),可在Python应用中用于实现发布—订阅模式,也就是观察者设计模式。
举例实现
例子使用继承实现观察者模式。首先实现一个Publisher,包含添加,删除及通知观察者等公用功能。DefaultFormatter类继承自Publisher,此类包含要被广播的数据,并在数据更新时调用通知观察者的方法。然后定义了两个观察者(HexFormatter和BinaryFormatter),观察者需要定义notify的方法及实现,以供广播者在数据更新时调用。完整代码文件observer.py
class Publisher:
def __init__(self):
self.observers = []
def add(self, observer):
if observer not in self.observers:
self.observers.append(observer)
else:
print("Failed to add: {}".format(observer))
def remove(self, observer):
try:
self.observers.remove(observer)
except ValueError:
print("Failed to remove: {}".format(observer))
def notify(self):
[o.notify(self) for o in self.observers]
class DefaultFormatter(Publisher):
def __init__(self, name):
Publisher.__init__(self)
self.name = name
self._data = 0
def __str__(self):
return "{}: '{}' has data = {}".format(type(self).__name__, self.name, self._data)
@property
def data(self):
return self._data
@data.setter
def data(self, new_value):
try:
self._data = new_value
except ValueError as e:
print("Error: {}".format(e))
else:
self.notify()
class HexFormatter:
def notify(self, publisher):
print("{}: '{}' has now hex data = {}".format(type(self).__name__, publisher.name, hex(publisher.data)))
class BinaryFormatter:
def notify(self, publisher):
print("{}: '{}' has now binary data = {}".format(type(self).__name__, publisher.name, bin(publisher.data)))
def main():
df = DefaultFormatter('test1')
print(df)
print()
hf = HexFormatter()
df.add(hf)
df.data = 3
print(df)
print()
bf = BinaryFormatter()
df.add(bf)
df.data = 21
print(df)
print()
df.remove(hf)
df.data = 13
print(df)
if __name__ == '__main__':
main()
运行结果:
DefaultFormatter: 'test1' has data = 0
HexFormatter: 'test1' has now hex data = 0x3
DefaultFormatter: 'test1' has data = 3
HexFormatter: 'test1' has now hex data = 0x15
BinaryFormatter: 'test1' has now binary data = 0b10101
DefaultFormatter: 'test1' has data = 21
BinaryFormatter: 'test1' has now binary data = 0b1101
DefaultFormatter: 'test1' has data = 13
Process finished with exit code 0