JDK 1.2 之后,Java 在 java.util 里提供了观察者模式的接口和实现类:观察者实现 Observer,被观察者继承 Observable。
观察者必须实现 void update(Observable o, Object arg)。读者可以很轻易地用这一对类型把推送关系搭起来。
一、Observer 与 Observable
update 的第一个参数是触发这次通知的对象,第二个是附加参数。做过桌面事件的会觉得眼熟:前者像事件源,后者像消息。
| 类型 |
角色 |
关键方法 |
Observer |
观察者 |
update(Observable o, Object arg) |
Observable |
被观察者 |
addObserver / setChanged / notifyObservers / deleteObserver |
二、读者订阅杂志
出版社出杂志时主动告知读者。先写被观察者 Publisher(通知前要 setChanged()):
package com.observer.observable;
import java.util.Observable;
public class Publisher extends Observable {
private String magazineName;
public String getMagazineName() {
return magazineName;
}
public void publish(String magazineName) {
this.magazineName = magazineName;
setChanged();
notifyObservers(this);
}
}
读者实现 Observer:
package com.observer.observer;
import java.util.Observable;
import java.util.Observer;
import com.observer.observable.Publisher;
public class Reader implements Observer {
@Override
public void update(Observable o, Object arg) {
Publisher p = (Publisher) o;
System.out.println("我要订阅" + p.getMagazineName());
}
}
原文第一段测试只调用了 publish,没有 addObserver,读者收不到消息。完整注册如下:
Publisher publisher = new Publisher();
Reader reader = new Reader();
publisher.addObserver(reader);
publisher.publish("极客之家");
这是一种主动推送:出版时通知已注册的读者。
三、为什么 notifyObservers(this)?
同一个观察者可以挂到不同被观察者上,传来的 Observable o 用来分辨消息是谁发的。再引入电视台:
package com.observer.observable;
import java.util.Observable;
public class TVStation extends Observable {
private String programmeName;
public void play(String programmeName) {
this.programmeName = programmeName;
setChanged();
notifyObservers(this);
}
public String getProgrammeName() {
return programmeName;
}
}
package com.observer.observer;
import java.util.Observable;
import java.util.Observer;
import com.observer.observable.Publisher;
import com.observer.observable.TVStation;
public class Reader implements Observer {
@Override
public void update(Observable o, Object arg) {
if (o instanceof Publisher) {
Publisher p = (Publisher) o;
System.out.println("我要订阅" + p.getMagazineName());
}
if (o instanceof TVStation) {
TVStation t = (TVStation) o;
System.out.println("我要收看" + t.getProgrammeName());
}
}
}
package com.observer.test;
import org.junit.Test;
import com.observer.observable.Publisher;
import com.observer.observable.TVStation;
import com.observer.observer.Reader;
public class TestCase {
@Test
public void register() {
Reader reader = new Reader();
Publisher publisher = new Publisher();
publisher.addObserver(reader);
TVStation tvStation = new TVStation();
tvStation.addObserver(reader);
publisher.publish("极客之家");
tvStation.play("色戒");
}
}
四、收完消息可以取消订阅
在 update 里调用 deleteObserver(this) 即可撤销对出版社的观察:
@Override
public void update(Observable o, Object arg) {
if (o instanceof Publisher) {
Publisher p = (Publisher) o;
p.deleteObserver(this);
System.out.println("我要订阅" + p.getMagazineName());
}
if (o instanceof TVStation) {
TVStation t = (TVStation) o;
System.out.println("我要收看" + t.getProgrammeName());
}
}
一句话总结:先 addObserver,再 setChanged + notifyObservers;传来的 Observable 用来分辨是谁在推,不看了就 deleteObserver。