java中的观察者模式
观察者模式的角色
1、观察者
持有一个方法,接收被观察者的通知
2、被观察者
持有观察者数组、被观察者状态发生变化可以通知观察者
java中利用 java.util.Observable
和 java.util.Observer
就能实现观察者模式
观察者实现Observer的update方法
被观察者继承Observable
Observable
Observable 持有 Observer的数组,Observable的状态改变了就会遍历Observer数组,分别通知Observer对象。
public class Observable {
private boolean changed = false; // 是否触发状态改变
private Vector<Observer> obs; // 观察者数组
public Observable() {
obs = new Vector<>();
}
/** 增加观察者 */
public synchronized void addObserver(Observer o) {
if (o == null)
throw new NullPointerException();
if (!obs.contains(o)) {
obs.addElement(o);
}
}
/** 删除观察者 */
public synchronized void deleteObserver(Observer o) {
obs.removeElement(o);
}
/** 通知观察者状态被触发 */
public void notifyObservers() {
notifyObservers(null);
}
public void notifyObservers(Object arg) {
Object[] arrLocal;
synchronized (this) {
if (!changed)
return;
arrLocal = obs.toArray();
clearChanged();
}
for (int i = arrLocal.length-1; i>=0; i--)
((Observer)arrLocal[i]).update(this, arg);
}
public synchronized void deleteObservers() {
obs.removeAllElements();
}
/** 设置状态为被触发 */
protected synchronized void setChanged() {
changed = true;
}
}
Observer
观察者接口
public interface Observer {
void update(Observable o, Object arg);
}
例子
学生是被观察者,老师和父母是观察者
如果学生在跑步,则通知老师和父母
public class AStudent extends Observable {
private String name;
public String getName() {
return name;
}
public AStudent(String name) {
super();
this.name = name;
}
public void running() {
String tip = name + " is running...";
System.out.println(tip);
this.setChanged();
this.notifyObservers(tip);
}
}
public class MParent implements Observer {
@Override
public void update(Observable o, Object arg) {
if(! (o instanceof AStudent)) {
throw new IllegalArgumentException("只接受AStudent");
}
AStudent s = (AStudent)o;
String string = String.format("MParent正在观察,被观察的人是%s ,描述:%s", s.getName(), arg);
System.out.println(string);
}
}
public class MTeacher implements Observer {
private String name;
public MTeacher(String name) {
super();
this.name = name;
}
@Override
public void update(Observable o, Object arg) {
// 采用异步 即使该观察者出现异常,不影响其他观察者
new Thread(()->{
if(! (o instanceof AStudent)) {
throw new IllegalArgumentException("只接受MStudent");
}
AStudent s = (AStudent)o;
String string = String.format("%s正在观察,被观察的人是 %s,描述:%s", name, s.getName(), arg);
System.out.println(string);
// throw new RuntimeException("1235");
}).start();
}
}
public class Demo {
public static void main(String[] args) {
AStudent student = new AStudent("小满");
student.addObserver(new MTeacher("语文老师"));
student.addObserver(new MTeacher("数学老师"));
student.addObserver(new MParent());
student.running();
}
}
/*
打印结果:
小满 is running...
MParent正在观察,被观察的人是小满 ,描述:小满 is running...
数学老师正在观察,被观察的人是 小满,描述:小满 is running...
语文老师正在观察,被观察的人是 小满,描述:小满 is running...
*/
注意事项
1、避免循环引用
2、如果顺序执行,某一观察者错误会导致系统卡壳,一般采用异步方式
观察者和发布订阅的区别
1、功能相同:都是一个对象发生变化,通知其他对象。
2、不同点:
角色不同:
- 观察者模式的角色:观察者和被观察者
被观察者持有观察者数组,代码不完成耦合 - 发布订阅模式:发布者,订阅者和代理人
发布者向代理人发布一个主题的消息
订阅者向代理人订阅一个主题
发布者和订阅者互不干涉,完全解耦。