写在前面的话:终于通读了一遍《Spring源码深度解析》,在Spring的消息发布(Message)模块使用了设计模式中的观察者模式的经典实现,所以趁此机会总结一些。
1. 业务场景
Spring框架中的就是使用了观察者模式实现事件的消息发布(Message)模块的。
2. 传统处理/观察者模式
2.1 什么是观察者模式
这一次讲的情节很简单,但是充满了谋略和斗争。大体的意思就是三国初期,曹刘孙三家在徐州联手消灭了吕布,但是自己也伤了元气。而在此时袁术得了传国玉玺,在淮南称帝,兵精将广,图谋不轨,对三家威胁很大。于是曹刘孙三家在一起开会,决定派遣一名超级间谍打入袁术身旁,监视他的一举一动,这样的话一旦袁术想干什么坏事,他们就可以知道并做出相应调整,知己知彼百战百胜。
计是好计,问题时派谁去当这个超级间谍呢?正当大家愁眉苦脸的时候,美女貂蝉自告奋勇,想当年董卓那么厉害都让灭了,何况一个小小的袁术?大家一听有道理,于是就把貂蝉献给了元素当了妃子。另外三家还各自派出了小奸细常住在淮南城内,他们的任务就是当联络员,貂蝉有什么情报总不能自己曹刘孙三家挨个跑着送吧?直接丢给各国联络员,然后让他们通知各自的主公就OK了,而三家只要一接到各自奸细的通知,就会立即做出反应。
还有一个小插曲,袁术虽然手下了貂蝉,但是对她看管很严,大大方法的把情报送出去不可能,逼不得已,貂蝉设计了一套密码来传送情报,虽然密码方法没有公开,但是她想总有人可以破解吧,没错,对诸葛亮来说就是小菜一碟,从此袁术穿什么内裤曹刘孙三家都知道的清清楚楚。
用观察者模式来分析下上面的故事
- 美女貂蝉:貂蝉在观察者模式中叫做被观察者(Subject),主要任务是独立的管理后台数据和业务逻辑,同时尽可能不影响前台可会断界面变化的影响。当然,还要负责登记或者住校各个观察者。在这个故事里,貂蝉仅仅维护了一个数据,就是情报——私有变量info;另外还有一个业务逻辑,是用来加密info的方法Reverse(String str) 。每次得到新的情报,她就会先加密,然后找到在自己这登记过的联络员,让这些联络员联系自己的主公应变。情报一旦发出去,貂蝉的任务就算完成了,具体曹刘孙三家怎么应对,是打是降还是另有办法,那是三家自己的事情,和貂蝉没有什么关系了。
- 曹孙刘三家:曹孙刘三家在观察者模式中叫做观察者,主要任务就是从界面上用各种方式即时反映出被观察者,所谓“各种方式”就是说字符、图形、声音都可以表示同样的数据,只是外在表示不同而已,本质都是一些数据,所谓“即时”,就是说只要被观察者发生变化,观察者也会立即跟着变化,用行话叫做刷新Update吧,在这个故事里,我们可以看到运行结果中,每次貂蝉有什么新的情报,三家都会在屏幕上显示出来,虽然简单,但是他们确实是用各自不同的方式表示了同样的数据。
观察者模式又称为发布/订阅模式,在对象之间定义了一对多的依赖关系,这样依赖,当一个对象,依赖它的对象会收到通知并自动更新。
3. 代码实现
3.1 创建被观察者
public interface Subject<T> {
/**
* 订阅者列表
*/
List<Observer> list = new ArrayList<Observer>();
/**
* 注册订阅者
*
* @param obs
*/
void registerObserver(T obs);
/**
* 移除订阅者
*
* @param obs
*/
void removeObserver(T obs);
/**
* 通知所有的观察者更新状态
*/
void notifyAllObservers();
}
3.2 创建被观察者实现
public class CurrentSubject implements Subject<Observer> {
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
// 被观察者值发生变化,请通知所有观察者
this.notifyAllObservers();
}
/**
* 注册订阅者
*
* @param obs
*/
@Override
public void registerObserver(Observer obs) {
list.add(obs);
}
/**
* 移除订阅者
*
* @param obs
*/
@Override
public void removeObserver(Observer obs) {
list.remove(obs);
}
/**
* 通知所有的观察者更新状态
*/
@Override
public void notifyAllObservers() {
for (Observer observer : list) {
// 更新每个观察者信息
observer.update(this);
}
}
}
3.3 创建观察者
public interface Observer {
/**
* 修改方法
*/
void update(Subject subject);
}
3.4 创建观察者实现
public class ObserverA implements Observer {
/**
* myState需要跟目标对象的state值保持一致!
*/
private int myState;
/**
* 修改方法
* 更新为和目标对象的值一致
* @param subject
*/
@Override
public void update(Subject subject) {
myState = ((CurrentSubject) subject).getState();
}
public int getMyState() {
return myState;
}
public void setMyState(int myState) {
this.myState = myState;
}
3.5 测试方法
public class Test {
public static void main(String[] args) {
CurrentSubject currentSubject = new CurrentSubject();
// 创建多个观察者
ObserverA a = new ObserverA();
ObserverA b = new ObserverA();
ObserverA c = new ObserverA();
// 将这三个观察者添加到subject对象的列表中
currentSubject.registerObserver(a);
currentSubject.registerObserver(b);
currentSubject.registerObserver(c);
// 改变subject的状态
currentSubject.setState(3000);
System.out.println("================第一次=================");
System.out.println(a.getMyState());
System.out.println(b.getMyState());
System.out.println(c.getMyState());
// 改变subject的状态
currentSubject.setState(30);
System.out.println("================第二次=================");
System.out.println(a.getMyState());
System.out.println(b.getMyState());
System.out.println(c.getMyState());
// 改变subject的状态
currentSubject.setState(99);
System.out.println("================第三次=================");
System.out.println(a.getMyState());
System.out.println(b.getMyState());
System.out.println(c.getMyState());
}
}
4. 观察者模式用途与优势
优点:解除观察者与被观察者之间的耦合。让耦合的双方都依赖抽象,而不是依赖具体。从而使得给子变化都不会影响另一边的变化。易于扩展,对于同一被观察者新增观察者无需修改原有代码。
缺点:依赖关系并未完全解除,抽象的被观察者仍然依赖观察者。使用观察者模式时需要开率开发效率和运行效率的问题,程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂,而且Java中消息的通知一般是顺序执行,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般采用异步实现。可能会引起多余的数据通知。
用途:1. Spring框架的Message消息广播功能;
2. Servlet中监听器的实现;
3. JDK中内置了观察者模式经典实现;
4. 聊天室程序的,服务器转发给所有客户端。