观察者一般可以看做是第三者,比如在学校上自习的时候,大家肯定都有过交头接耳、各种玩耍的经历,这时总会有一个“放风”的小伙伴,当老师即将出现时及时“通知”大家老师来了。再比如,拍卖会的时候,大家相互叫价,拍卖师会观察最高标价,然后通知给其它竞价者竞价,这就是一个观察者模式。
观察者模式又叫做 发布—订阅模式 ,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知和更新,观察者模式提供了一个订阅模型,其中对象订阅事件并在发生时得到通知,这种模式是事件驱动的编程基石,它有利益于良好的面向对象的设计
观察者模式(前端)
定义:对象间的一种一对多的依赖关系。
需求:当一个对象的状态发生变化时,所有依赖于他的对象都将得到通知。
优点:时间上的解耦,对象之间的解耦。
实现:
指定好谁充当发布者;
给发布者添加一个缓存列表,用于存放回调函数以便通知订阅者;
发布消息的时候,发布者会遍历这个缓存列表,依次触发里面存放的订阅者回调函数。
下面举个例子,比如我们给页面中的一个dom节点绑定一个事件,其实就可以看做是一种观察者模式:
1 | document.body.addEventListener("click", function() { |
总结:在上面的例子中,需要监听用户点击 document.body 的动作,但是我们是没办法预知用户将在什么时候点击的,因此我们订阅了 document.body 的 click 事件,当 body 节点被点击时,body 节点便会向订阅者发布 “Hello World” 消息。
观察者模式(后端)
其中,Subject类是主题,它把所有对观察者对象的引用文件存在了一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供了一个接口,可以增加和删除观察者对象;Observer类是抽象观察者,为所有的具体观察者定义一个接口,在得到主题的通知时更新自己;ConcreteSubject类是具体主题,将有关状态存入具体观察者对象,在具体主题内部状态改变时,给所有登记过的观察者发出通知;ConcreteObserver是具体观察者,实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题的状态相协同。
1 | public class Subject { |
1 | 1 public interface Observer { |
1 | public class ConcreteSubject extends Subject { |
1 | public class ConcreteObserver implements Observer { |
1 | public class Client { |
运行结果:
观察者模式的应用
1. 何时使用
一个对象状态改变,所有的依赖对象都将得到通知
2. 方法
使用面向对象技术
3. 优点
观察者和被观察者是抽象耦合的
建立了一套触发机制
4. 缺点
如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很多时间
如果观察者和观察目标间有循环依赖,可能导致系统崩溃
没有相应的机制让观察者知道所观察的目标对象是怎么发生变化的
5. 使用场景
关联行为场景
事件多级触发场景
跨系统的消息变换场景,如消息队列的处理机制
6. 应用实例
手机丢了,委托别人给其他人发消息通知
通知老师/老板来了
拍卖,拍卖师观察最高标价,然后通知给其它竞价者竞价
在一个目录下建立一个文件,会同时通知目录管理器增加目录,并通知磁盘减少空间,文件是被观察者,目录管理器和磁盘管理器是观察者
猫叫了一声,吓着了老鼠,也惊到了主人,猫是被观察者,老鼠和人是观察者
7. 注意事项
避免循环引用
如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式