观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有的观察者对象,让他们可以自动更新自己。
观察者模式的类图:
- 抽象主题(Subject):主题对象可以把所有的观察者对象保存在一个容器中,并且抽象主题提供删除和注册观察者的方法。
- 抽象观察者(Observer):是所有具体的观察者的接口,在得到抽象主题发生变化时更新自己的状态。
- 具体主题(ConcreteSubject):在具体主题的内部状态发生变化时,给所有登记过的观察者发出通知。
- 具体观察者(ConcreteObserver):存储于主题相符的状态,具体观察者实现抽象观察者的更新方法。
抽象主题:
public interface Subject {
//注册观察者
public void attach(Observer observer);
//移除观察者
public void remove(Observer observer);
//通知所有观察者已经更新状态
void notifyAllObserver();
}
具体主题:
public class ConcreteSubject implements Subject {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
// 主题状态改变,通知所有观察者
this.notifyAllObserver();
}
// 使用ArrayList管理观察者
private ArrayList<Observer> list = new ArrayList<Observer>();
@Override
public void attach(Observer observer) {
list.add(observer);
}
@Override
public void remove(Observer observer) {
list.remove(observer);
}
@Override
public void notifyAllObserver() {
// 通知所有观察者
for (Observer o : list) {
o.update(this);
}
}
}
抽象观察者:
public interface Observer {
// 更新状态
public void update(Subject subject);
}
具体观察者:
public class ConcreteObserver implements Observer {
private int myState;
private String myName;
public ConcreteObserver(String _name) {
this.myName = _name;
}
@Override
public void update(Subject subject) {
// 得到状态
myState = ((ConcreteSubject) subject).getState();
System.out.println("观察者: " + this.myName + ":--->" + myState);
}
}
客户端类:
public class Client {
public static void main(String[] args) {
// 得到具体主题
ConcreteSubject cs = new ConcreteSubject();
ConcreteObserver co1 = new ConcreteObserver("观察者甲");
ConcreteObserver co2 = new ConcreteObserver("观察者乙");
ConcreteObserver co3 = new ConcreteObserver("观察者丙");
// 注册观察者
cs.attach(co1);
cs.attach(co2);
cs.attach(co3);
System.out.println("具体主题状态改变为 3");
System.out.println("******************");
cs.setState(3);
System.out.println("具体主题状态改变为 6");
System.out.println("******************");
cs.setState(6);
}
}
运行客户端类控制台会输出以下结果:
具体主题状态改变为 3
******************
观察者: 观察者甲:--->3
观察者: 观察者乙:--->3
观察者: 观察者丙:--->3
具体主题状态改变为 6
******************
观察者: 观察者甲:--->6
观察者: 观察者乙:--->6
观察者: 观察者丙:--->6
使用观察者模式应该重点注意两个问题:
- 广播链的问题: 如果一个对象既是观察者也是被观察者,比如数据库中的触发器,A表插入数据后,B表会同时插入数据;B表插入数据后,C表会同时插入数据......这样数据库基本就毁掉了!建议在观察者模式中,最多出现一个对象同时作为观察者和被观察者(消息最多传递两次)。
- 异步处理问题:如果观察者很多,处理时间比较长,只能采用异步处理的方式,这是需要考虑线程安全以及队列的问题。
观察者模式的优缺点:
优点:
- 观察者模式在观察者和被观察者之间建立一个抽象的耦合。被观察者角色所知道的只是一个具体观察者的聚集,每一个具体观察者都符合抽象观察者的接口。被观察者并不“认识”具体的观察者。
- 观察者模式支持广播通信,被观察者会向所有登记过的观察者发出通知。
缺点:
- 如果一个被观察者有很多直接或者间接的观察者,一一通知观察者会花费很多时间。
- 如果被观察者之间存在循环依赖,那么会出现循环调用,导致系统崩溃。
- 如果被观察者通知观察者是采用其他线程进行时,应该避免线程安全问题。
- 观察者虽然可以知道被观察者发生变化,但是不能知晓具体是怎么发生变化的。