OBSERVER(观察者) ———— 对象行为型模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
Observer模式中的关键对象是目标(subject)和观察者(observer)。一个目标可以有任意数目的依赖它的观察者。一旦目标的状态发生改变,所有的观察者都得到通知。作为对这个通知的响应,每个观察者都将查询目标以使其状态与目标的状态同步。
这种交互也称为发布-订阅(publish-subscribe)。目标是通知的发布者。它发出通知时并不需要知道谁是它的观察者。可以有任意数目的观察者订阅并接受通知。
在以下任一情况下可以使用观察者模式 ① 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。 ② 当对一个对象的改变需要同时改变其它对象,而不知道具体有多少对象有待改变。 ③ 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之,你不希望这些对象是紧密耦合的。
观察者模式结构图.png
① 目标和观察者的抽象耦合:一个目标所知道的仅仅是它又一系列观察者,每个都符合抽象的Observer类的简单接口。目标不知道任何一个观察者属于哪一个具体的类。这样目标和观察者之间的耦合是抽象的和最小的。
② 支持广播通信:不像通常的请求,目标发送的通知不需指定它的接收者。通知被自动广播给所有已向该目标对象登记的有关对象。目标对象并不关心到底有多少对象对自己感兴趣;它唯一的责任就是通知它的各观察者。这给了你在任何时刻增加和删除观察者的自由。处理还是忽略一个通知取决于观察者。
① 意外的更新:因为一个观察者并不知道其他观察者的存在,它可能对改变目标的最终代价一无所知。在目标上一个看似无害的操作可能会引起一系列对观察者以及依赖于这些观察者的那些对象的更新。此外,如果依赖准则的定义或维护不当,常常会引起错误的更新,这种错误通常很难捕捉。
a) 推模型是假定目标对象知道观察者需要的数据;而拉模型是目标对象不知道观察者具体需要什么数据,没有办法的情况下,干脆把自身传给观察者,让观察者自己去按需取值。 b) 推模型可能会使得观察者对象难以复用,因为观察者定义的update方法是按需而定义的,可能无法兼顾没有考虑到的使用情况。这就意味着出现新情况的时候,就可能需要提供新的update方法,或者是干脆重新实现观察者。 c) 而拉模型就不会造成这样的情况,因为拉模型下,update方法的参数是目标对象本身,这基本上是目标对象能传递的最大数据集合了,基本上可以适应各种情况的需要。
Java API 有内置的观察者模式。java.util包(package)内包含最基本的Observer接口与Observable类,这和我们的Subject接口与Observer接口很相似。Observer接口与Observable类使用上更方便,因为许多功能都已经事先准备好了。你甚至可以使用推或拉的方式传送数据。
Java API 内置观察者模式.png
我们可以通过继承Observable类和实现Observer接口来分别实现主题和观察者。然后通过调用Observable对象的addObserver()方法来添加观察者,调用deleteObserver()来移除一个观察者。
Q:Observable要如何送出通知? A: 需要两个步骤: ① 先调用setChanged()方法,标记状态已经改变的事实。 ② 然后调用两种notifyObservers方法中的一个: a) notifyObsrvers() ———— 用于拉模型; b) notifyObserves(Object arg) ———— 用于推模型;
Q:Observer如何接受通知? A:同以前一样,观察者实现了更新的方法,但是方法的签名不太一样: void update(Observable o, Object arg); 参数 Observable o :主题本身当作第一个变量,好让观察者知道是哪个主题通知它的。若是拉模式,则需要通过该参数来获取所需的数据。 参数 Object arg :若Observable通过notifyObsrvers()方式发送通知,则该参数为null;若Observable通过notifyObserves(Object arg)方式发送通知,则该参数为推送过来的数据对象。
Q:为什么在Observable发送notifyObservers前一定要调动setChanged()方法来标记状态已经改变的事实了? A:从notify的源码能看出,若状态标志changed未被设置为true,则不会进行对Observer的notify操作了。而setChanged()方法就是来完成对状态标志changed的设置的。 而这么做的好处在于,让你在更新观察者时有更多的弹性,你可以更适当地通知观察者。
《Head First 设计模式》 《设计模式:可复用面向对象软件的基础》 《研磨设计模式》