观察者模式
前言
- 概念
观察者模式主要用于1∶N的通知。当一个对象(目标对象Subject或Objservable)的状态变化时,他需要及时告知一系列对象(观察者对象,Observer),令他们做出响应。
通知观察者的方式:
推——每次都会把通知以广播方式发送给所有观察者,所有观察者只能被动接收。
拉——观察者直到有情况即可。至于什么时候获取内容,获取什么内容,都可以自主决定。 - 结构
抽象主题角色(Subject):主题角色把所有观察者角色的引用保存在一个列表里,又被成为被观察者角色,可以用抽象类或者接口实现,也可用具体实现类实现。
抽象观察者(Observer):为所有的具体观察者定义一个接口,在得到消息时更新自己。
具体主题角色(ConcreteSubject):具体主题保存对具体观察者对象有用的内部状态,在这种内部状态改变时,给其观察者发出一个通知。
具体观察者角色(ConcreteObserver):用于保存一个指向具体主题对象的引用,和一个与主题的状态相符的状态。
一、具体实现
1.抽象主题角色
public abstract class Subject {
protected List<Observer> list = new ArrayList<Observer>();
public abstract void registerObserver(Observer obs);
public abstract void removeObserver(Observer obs);
//通知所有的观察者更新状态
public abstract void notifyAllObservers();
}
2.抽象观察者
public interface Observer {
void update(Subject subject);
}
3.具体主题角色
public class ConcreteSubject extends Subject {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
//主题对象(目标对象)值发生了变化,请通知所有的观察者
this.notifyAllObservers();
}
@Override
public void registerObserver(Observer obs) {
super.list.add(obs);
}
@Override
public void removeObserver(Observer obs) {
super.list.remove(obs);
}
@Override
public void notifyAllObservers() {
for (Observer obs : list) {
obs.update(this);
}
}
}
4.具体观察者
public class ObserverA implements Observer {
private int myState; //myState需要跟目标对象的state值保持一致!
@Override
public void update(Subject subject) {
myState = ((ConcreteSubject)subject).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
5.测试类
public class Client {
public static void main(String[] args) {
//目标对象
ConcreteSubject subject = new ConcreteSubject();
//创建多个观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//将这三个观察者添加到subject对象的观察者队伍中
subject.registerObserver(obs1);
subject.registerObserver(obs2);
subject.registerObserver(obs3);
//改变subject的状态
subject.setState(3000);
System.out.println("########################");
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
//改变subject的状态
subject.setState(30);
System.out.println("########################");
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
}
}
结果:
二、JdK内置观察者
1.具体主题角色
(继承Observable类)
//目标对象
public class ConcreteSubject extends Observable {
private int state;
public void set(int s){
state = s; //目标对象的状态发生了改变
setChanged(); //表示目标对象已经做了更改
notifyObservers(state); //通知所有的观察者
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
}
2.具体观察者
(实现Observer接口)
public class ObserverA implements Observer {
private int myState;
@Override
public void update(Observable o, Object arg) {
myState = ((ConcreteSubject)o).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
}
3.测试类
public class Client {
public static void main(String[] args) {
//创建目标对象Obserable
ConcreteSubject subject = new ConcreteSubject();
//创建观察者
ObserverA obs1 = new ObserverA();
ObserverA obs2 = new ObserverA();
ObserverA obs3 = new ObserverA();
//将上面三个观察者对象添加到目标对象subject的观察者容器中
subject.addObserver(obs1);
subject.addObserver(obs2);
subject.addObserver(obs3);
//改变subject对象的状态
subject.set(3000);
System.out.println("===============状态修改了!");
//观察者的状态发生了变化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
subject.set(600);
System.out.println("===============状态修改了!");
//观察者的状态发生了变化
System.out.println(obs1.getMyState());
System.out.println(obs2.getMyState());
System.out.println(obs3.getMyState());
}
}
结果:
三、应用场景
- 聊天室程序的,服务器转发给所有客户端
- 网络游戏(多人联机对战)场景中,服务器将客户端的状态进行分发
- 邮件订阅
- Servlet中,监听器的实现
- Android中,广播机制
- JDK的AWT中事件处理模型,基于观察者模式的委派事件模型(Delegation Event
Model)
• 事件源----------------目标对象
• 事件监听器------------观察者 - 京东商城中,群发某商品打折信
总结
- 优点
1.观察者模式在观察者和被观察者之间建立了一个抽象的耦合。
2.观察者模式支持广播通信。 - 缺点
1.如果一个被观察者对象有很多的直接和间接的观察者的话,将所有的观察者都通知到会花费很长时间。
2.如果在被观察者之间有循环依赖的话,被观察者会触发它们之间进行循环调用,导致系统崩溃。
3.如果对观察者的通知是通过另外的线程进行异步投递的话,系统必须保证投递是以自恰的方式进行的。
4.虽然观察者模式可以随时使观察者知道所观察的对象发生变化,但是观察者模式没有相应的机制使观察者知道所观察的对象是怎么样发生变化的。 - 场景
1.聊天室程序的创建。服务器创建好后,A,B,C三个客户端连上来公开聊天。A向服务器发送数据,服务器端聊天数据改变。我们希望将这些聊天数据分别发给其他在线的客户。也就是说,每个客户端需要更新服务器端得数据。
2.网站上,很多人订阅了”java主题”的新闻。当有这个主题新闻时,就会将这些新闻发给所有订阅的人。
3.大家一起玩CS游戏时,服务器需要将每个人的方位变化发给所有的客户