观察者模式在实际中用的非常多,这里的subject/observer(主题/观察者)也被称为publisher/subscriber
(发布者/订阅者)。我个人觉得发布订阅这种叫法更好理解。比如微信里订阅公众号,当公众号发布新消息时,你的微信会自动收到消息推送。这个听起来很神奇,居然可以自动收到消息。但其实弄懂这个模式后你会发现这个其实很简单。
为什么订阅者可以自动收到消息?其实说白了,就是发布者对象里有一个订阅者对象的list,当发布者发布新消息时,循环这个订阅者的list,并调用订阅者对象的update方法。也就是说,并不是发布者来更新订阅者的,而是发布者调用订阅者自己的update方法让订阅者自己更新的。怎么样?是不是很简单。
原理清楚后,这个模式也就基本清楚了。首先抽象出两个接口,分别是发布者和订阅者的接口。发布者接口为Subject,由于里面有一个订阅者list,所以有注册订阅者(registerObserver),删除订阅者(removeObserver),通知订阅者(notifyObserver)三个方法。订阅者接口为Observer,里面有update方法,当发布者调用notifyObserver方法时,会调用订阅者的update方法来更新自己。
这样设计以后,需要增加一个订阅者时,只需要注册进发布者的list里即可,不需要修改之前的代码,这样就遵守了开放(易于扩展)封闭(不修改原来的代码)原则。
下面来说一下使用java内置的观察者模式。
Java已经帮我们准备好了上面所说的两个接口,分别是Observable类(注意这是一个抽象类,对应上面的Subject接口)和Observer接口(对应上面的Observer接口)。Observerable之所以设计为抽象类,是因为它把registerObserver,removeObserver,notifyObserver这三个方法都已经封装好了,不用我们自己实现,更方便。Observer里与上面一样,只有一个update方法。
需要注意的是,在调用notifyObserver方法之前,必须先调用setChanged()方法,来告诉java状态已经改变。我想可能大多数人跟我一样觉得这样做很麻烦,没什么必要。我调用notifyObserver不就是默认已经是要改变了吗?看了《Head First设计模式》一书后,才发现这样做是很有必要的。setChanged()方法可以让你在更新观察者时,有更多的弹性。你可以更适当的通知观察者。有的时候,并不是每次更新都需要通知观察者。
传递数据有两种方法,一种是“推”(push)给观察者,另一种是观察者来“拉”(pull)数据。
看下update方法的两个参数
Update(Observerable o, Object arg)
当你想推数据时,将数据放到arg参数中,当你想拉数据时,从Observerable 对象(这是一个发布者的对象)里取即可。
还有一点需要注意的是,用java内置的观察者模式,每个观察者被通知的顺序是随机的,如果你的程序对顺序有要求的话,你就得自己实现观察者模式了。
下面是一个完整的例子。
package com.java.api.observer; import java.util.Observable; /** * 主题 */ public class Person extends Observable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; this.setChanged(); this.notifyObservers(name); } }
package com.java.api.observer; import java.util.Observable; import java.util.Observer; /** * 观察者1 */ public class Observer1 implements Observer { public void update(Observable o, Object arg) { String name = (String) arg; System.out.println("Observer1 have recived changes!" + " name is " + name); } }
package com.java.api.observer; import java.util.Observable; import java.util.Observer; /** * 观察者2 */ public class Observer2 implements Observer { public void update(Observable o, Object arg) { String name = (String) arg; System.out.println("Observer2 have recived changes!" + " name is " + name); } }
package com.java.api.observer; import java.util.Observable; import java.util.Observer; /** * 观察者3 */ public class Observer3 implements Observer { public void update(Observable o, Object arg) { String name = (String) arg; System.out.println("Observer3 have recived changes!" + " name is " + name); } }
package com.java.api.observer; /** * 测试类 */ public class ObserverTest { public static void main(String[] args) { Person p1 = new Person(); p1.addObserver(new Observer1()); p1.addObserver(new Observer2()); p1.addObserver(new Observer3()); p1.setName("dfs"); p1.setName("dfs222"); } }