设计模式之观察者
引子
之前写一个android app的时候,需要实现这样一个流程,子线程中进行文件遍历,结束后将结果保存到数据类中,然后数据类通知主线程。我希望数据类可以主动向主线程发送更新,而不必在主线程中编程。这就非常适合采用观察者模式。因为观察者模式就是用来处理多个对象对一个对象的状态有依赖的场景的。
观察者模式
一般情况下,当对象 b 依赖对象 a 的状态变化时,最直接的办法是 a 主动更新 b。
class A {
//当 A 的对象的状态发生变化时调用此方法
public void onStateChanged(){
b.update();
}
}
上面的实现最大的问题时不利于扩展,试想,未来如果有更多的对象依赖 a 的状态,总是得修改 class A。这样紧密的耦合非常不利于项目的扩展。
观察者模式尝试去解决这样的问题。它把 a 这样持有数据的对象称为可观察对象,或者称为主题,把 b 这样依赖别的对象的状态来控制行为的称为观察者。主题持有数据以及观察者的名单,并公开注册和注销观察者的方法,以便程序运行时动态的更新观察者列表。 观察者则暴露一个统一的方法供主题在更新消息时调用。
具体的主题和观察者要做到类型无关,方法规范,最好都实现相应的接口。如下:
public interface Subject {
public void subscribe(Observer ob);
public void unsubscribe(Observer ob);
public void notifyObservers();
}
public interface Observer {
public void update();
}
下面是主题和观察者的具体实现: 新闻和观众
public class News implements Subject {
public List<Observer> observers = new ArrayList<Observer>();
@Override
public void subscribe(Observer ob) {
this.observers.add(ob);
}
@Override
public void unsubscribe(Observer ob) {
this.observers.remove(ob);
}
@Override
public void notifyObservers() {
Iterator<Observer> it = this.observers.iterator();
while(it.hasNext()){
it.next().update();
}
}
public void update(){
this.notifyObservers();
}
}
public class Person implements Observer {
private String name;
public Person(String name){
this.name = name;
}
@Override
public void update() {
System.out.println(name + " has been informed");
}
}
测试用类:
public class TestOb {
public static void main(String[] args){
News daily = new News();
Person tom = new Person("Tom");
Person jason = new Person("Jason");
daily.subscribe(tom);
daily.subscribe(jason);
daily.update();
}
}
运行结果:
Tom has been informed
Jason has been informed
上面的News和Person,并没有直接的关系。News并不关心现在有多少人订阅或者取消订阅,当消息来临时,只是对订阅列表中的人发送消息即可。而订阅的人也不必关心什么时候消息会来,只需要在update方法中对消息做各自的行为就可以了。这里News并没有存储数据,也没有推送具体的消息。如果需要的话,尽管在通知(update)订阅者时携带这些数据就好了。
JDK 中的实现
jdk中也有对观察者模式的实现,java.util.Observable(可观察类,即主题)和 java.util.Observer
public class Observable {
public Observable()
public void addObserver(Observer o)
public void deleteObserver(Observer o)
protected setChanged() //标记此对象为已改变对象
public void notifyObservers()
public void notifyObservers(Object arg)
}
- Observable 的主要方法都是线程保护的。
- Observable 提供了两个通知观察者的方法 notifyObservers,区别为是否发送参数给观察者。
- setChanged 方法设置改变状态,如果状态为没有改变,即使调用 notifyObservers 也不会发送消息给观察者。注意此方法是 protected,一般在设置完状态后调用。
- 当消息发送完毕后,Observable 会自动将是否改变这个状态位重置为false。
public interface Observer {
void update(Observable o, Object arg)
}
Observer 接口比较简单,只有一个 update 方法。可以接收Observable 对象和额外的参数。
例子
下面是一个例子,订阅公众号。
import java.util.Observable;
public class OfficialAccount extends Observable{
private String msg;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
this.setChanged();
this.notifyObservers(msg);
}
}
package com.icepin.ct.patterns.ob;
import java.util.Observable;
import java.util.Observer;
public class PersonalAccount implements Observer{
@Override
public void update(Observable o, Object arg) {
System.out.println("Got msg " + arg.toString());
}
}
测试类
package com.icepin.ct.patterns.ob;
public class TestObInJdk {
public static void main(String[] args){
OfficialAccount myOC = new OfficialAccount();
PersonalAccount remind = new PersonalAccount();
PersonalAccount mona = new PersonalAccount();
PersonalAccount nita = new PersonalAccount();
myOC.addObserver(remind);
//myOC.addObserver(remind);
myOC.addObserver(mona);
myOC.addObserver(nita);
myOC.setMsg("欢迎订阅微信公众号 -去你的-");
}
}
运行结果:
Got msg 欢迎订阅微信公众号 -去你的-
Got msg 欢迎订阅微信公众号 -去你的-
Got msg 欢迎订阅微信公众号 -去你的-
不足
jdk 中的实现相比前文中我的实现健全了很多,加入和标志位,线程保护,可传递的参数,等等。但是由于Observable是一个类而不是接口,java中不支持多重继承,这就在灵活性上有些不足。实际的项目中还是要根据需要使用jdk或者自造观察者模型。
总结
观察者模式用于解决多个对象对一个对象的状态依赖问题,通过注册和注销方法,主题能够保存观察者名单,并在未来反馈消息。观察者模式在jdk中有实现,但灵活性不足,可根据情况实现适合项目情况的模式。