python实现观察者模式

python实现观察者模式

 

 

前言python

  有时,咱们但愿在一个对象的状态改变时更新另一组对象。在MVC模式中有这样一个非 经常见的例子,假设在两个视图(例如,一个饼图和一个电子表格)中使用同一个模型的数据, 不管什么时候更改了模型,都须要更新两个视图。这就是观察者设计模式要处理的问题(请参考 [Eckel08,第213页])。   观察者模式描述单个对象(发布者,又称为主持者或可观察者)与一个或多个对象(订阅者, 又称为观察者)之间的发布 — 订阅关系。在MVC例子中,发布者是模型,订阅者是视图。然而, MVC并不是是仅有的发布 — 订阅例子。信息聚合订阅(好比,RSS或Atom)是另外一种例子。许多读 者一般会使用一个信息聚合阅读器订阅信息流,每当增长一条新信息时,他们就能自动地获取到 更新。   观察者模式背后的思想等同于MVC和关注点分离原则背后的思想,即下降发布者与订阅者 之间的耦合度,从而易于在运行时添加/删除订阅者。此外,发布者不关心它的订阅者是谁。它 只是将通知发送给全部订阅者(请参考[GOF95,第327页])。

 

介绍

意图:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,全部依赖于它的对象都获得通知并被自动更新。设计模式

主要解决:一个对象状态改变给其余对象通知的问题,并且要考虑到易用和低耦合,保证高度的协做。网络

什么时候使用:一个对象(目标对象)的状态发生改变,全部的依赖对象(观察者对象)都将获得通知,进行广播通知。app

如何解决:使用面向对象技术,能够将这种依赖关系弱化。异步

关键代码:在抽象类里有一个 ArrayList 存放观察者们。网站

应用实例: 一、拍卖的时候,拍卖师观察最高标价,而后通知给其余竞价者竞价。 二、西游记里面悟空请求菩萨降服红孩儿,菩萨洒了一地水招来一个老乌龟,这个乌龟就是观察者,他观察菩萨洒水这个动做。spa

优势: 一、观察者和被观察者是抽象耦合的。 二、创建一套触发机制。设计

缺点: 一、若是一个被观察者对象有不少的直接和间接的观察者的话,将全部的观察者都通知到会花费不少时间。 二、若是在观察者和观察目标之间有循环依赖的话,观察目标会触发它们之间进行循环调用,可能致使系统崩溃。 三、观察者模式没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的,而仅仅只是知道观察目标发生了变化。3d

使用场景:code

  • 一个抽象模型有两个方面,其中一个方面依赖于另外一个方面。将这些方面封装在独立的对象中使它们能够各自独立地改变和复用。
  • 一个对象的改变将致使其余一个或多个对象也发生改变,而不知道具体有多少对象将发生改变,能够下降对象之间的耦合度。
  • 一个对象必须通知其余对象,而并不知道这些对象是谁。
  • 须要在系统中建立一个触发链,A对象的行为将影响B对象,B对象的行为将影响C对象……,可使用观察者模式建立一种链式触发机制。

注意事项: 一、JAVA 中已经有了对观察者模式的支持类。 二、避免循环引用。 三、若是顺序执行,某一观察者错误会致使系统卡壳,通常采用异步方式。

 

 

应用案例

当咱们但愿在一个对象(主持者/发布者/可观察者)发生变化时通知/更新另外一个或多个对象
的时候,一般会使用观察者模式。观察者的数量以及谁是观察者可能会有所不一样,也能够(在运
行时)动态地改变。
  能够想到许多观察者模式在其中有用武之地的案例。本章开头已提过这样的一个案例,就是
信息聚合。不管格式为RSS、Atom仍是其余,思想都同样:你追随某个信息源,当它每次更新时,
你都会收到关于更新的一个通知(请参考[Zlobin13,第60页])。

  一样的概念也存在于社交网络。若是你使用社交网络服务关联了另外一我的,在关联的人更新
某些内容时,你能收到相关通知,不论这个关联的人是你关注的一个Twitter用户,Facebook上的
一个真实朋友,仍是LinkdIn上的一位同事。
  事件驱动系统是另外一个可使用(一般也会使用)观察者模式的例子。在这种系统中,监听
者被用于监听特定事件。监听者正在监听的事件被建立出来时,就会触发它们。这个事件能够是
键入(键盘的)某个特定键、移动鼠标或者其余。事件扮演发布者的角色,监听者则扮演观察者
的角色。在这里,关键点是单个事件(发布者)能够关联多个监听者(观察者),请参考网页
[t.cn/Rqr1Xgj]。

 

实现

  咱们将实现一个数据格式化程序。这里描述的想法来源于ActiveState网站上观察者
模式用法的Python代码实现(请参考网页[t.cn/Rqr1SDO])。默认格式化程序是以十进制格式展
示一个数值。然而,咱们能够添加/注册更多的格式化程序。这个例子中将添加一个十六进制格
式化程序和一个二进制格式化程序。每次更新默认格式化程序的值时,已注册的格式化程序就会
收到通知,并采起行动。在这里,行动就是以相关的格式展现新的值。
  在一些模式中,继承能体现自身价值,观察者模式是这些模式中的一个。咱们能够实现一个
基类 Publisher ,包括添加、删除及通知观察者这些公用功能。 DefaultFormatter 类继承自
Publisher ,并添加格式化程序特定的功能。咱们能够按需动态地添加删除观察者。下面的类图
展现了一个使用两个观察者( HexFormatter 和 BinaryFormatter )的示例。注意,由于类图
是静态的,因此没法展现系统的整个生命周期,只能展现某个特定时间点的系统状态。

 

 

# observers存储观察者 # add()方法注册一个新的观察者 # remove()方法注销一个已有的观察者 # notify() 方法则在变化发生时通知全部观察者。
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] # __str__() 方法返回关于发布者名称和 _data 值的信息。 # 第一个使用 @property 修饰器来提供 _data 变量的读访问方式。这样,咱们就能使用 object.data 来替代 object.data() 。 # 第二个 data() 更有意思。它使用了 @setter 修饰器,该修饰器会在每次使用赋值操做符( = ) 为 _data 变量赋新值时被调用。
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 = int(new_value) except ValueError as e: print('Error: {}'.format(e)) else: print("修改了") self.notify() # 下一步是添加观察者。 HexFormatter 和 BinaryFormatter 的功能很是类似。惟一的不一样 # 在于如何格式化从发布者那获取到的数据值,即分别以十六进制和二进制进行格式化。
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 bin data = {}".format(type(self).__name__, publisher.name, bin(publisher.data))) def main(): df = DefaultFormatter('test1') hf = HexFormatter() bf = BinaryFormatter() #添加观察者
 df.add(hf) df.add(bf) #每次修改发布者的值,observers里面的观察者会调用notify()方法
    df.data = 1 df.data = 10
    #当把某个观察者踢出observers,当修改发布者的data值就不会通知这个观察者
 df.remove(hf) df.data = 6

if __name__ == '__main__': main()
相关文章
相关标签/搜索