观察者模式【Observer Pattern 】
观察者模式以秦国时期李斯监视韩非子为例。
先看下类图:
被观察者:
public interface IHanFeiZi {
//韩非子也是人,也要吃早饭的
public void haveBreakfast();
//韩非之也是人,是人就要娱乐活动
public void haveFun();
}
被观察者的实现类:
public class HanFeiZi implements IHanFeiZi{
//韩非子是否在吃饭,作为监控的判断标准
private boolean isHaveBreakfast = false;
//韩非子是否在娱乐
private boolean isHaveFun = false;
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
this.isHaveBreakfast =true;
}
//韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.isHaveFun = true;
}
//以下是bean的基本方法,getter/setter,不多说
public boolean isHaveBreakfast() {
return isHaveBreakfast;
}
public void setHaveBreakfast(boolean isHaveBreakfast) {
this.isHaveBreakfast = isHaveBreakfast;
}
public boolean isHaveFun() {
return isHaveFun;
}
public void setHaveFun(boolean isHaveFun) {
this.isHaveFun = isHaveFun;
}
}
观察者的接口:
public interface ILiSi {
//一发现别人有动静,自己也要行动起来
public void update(String context);
}
观察者的实现类:
public class LiSi implements ILiSi{
//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
public void update(String str){
System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");
this.reportToQiShiHuang(str);
System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
}
//汇报给秦始皇
private void reportToQiShiHuang(String reportContext){
System.out.println("李斯:报告,秦老板!韩非子有活动了--->"+reportContext);
}
}
全部搞好上面了,就要来个监测程序了。
class Watch extends Thread{
private HanFeiZi hanFeiZi;
private LiSi liSi;
private String type;
//通过构造函数传递参数,我要监控的是谁,谁来监控,要监控什么
public Watch(HanFeiZi _hanFeiZi,LiSi _liSi,String _type){
this.hanFeiZi =_hanFeiZi;
this.liSi = _liSi;
this.type = _type;
}
@Override
public void run(){
while(true){
if(this.type.equals("breakfast")){ //监控是否在吃早餐
//如果发现韩非子在吃饭,就通知李斯
if(this.hanFeiZi.isHaveBreakfast()){
this.liSi.update("韩非子在吃饭");
//重置状态,继续监控
this.hanFeiZi.setHaveBreakfast(false);
}
}else{//监控是否在娱乐
if(this.hanFeiZi.isHaveFun()){
this.liSi.update("韩非子在娱乐");
this.hanFeiZi.setHaveFun(false);
}
}
}
}
}
这样子是可以完成对韩非子的监测,但仔细想想,就不行了,这样子while(true)开启死循环来进行监测,服务器肯定是受不了的,而且监测的对象只有一个,你以为你是秦始皇。。。还是大出血的修改一下好:韩非子是大人物,肯定会有很多间谍监视的,于是观察者应该提炼出来,观察者要观察的也不只是人家的吃法娱乐吧,故也提炼出来:
观察者:
public interface Observable {
//增加一个观察者
public void addObserver(Observer observer);
//删除一个观察者,——我不想让你看了
public void deleteObserver(Observer observer);
//既然要观察,我发生改变了他也应该用所动作——通知观察者
public void notifyObservers(String context);
}
被观察者实现上面的接口:
public class HanFeiZi implements Observable{
//定义个变长数组,存放所有的观察者
private ArrayList<Observer> observerList = new ArrayList<Observer>();
//增加观察者
public void addObserver(Observer observer){
this.observerList.add(observer);
}
//删除观察者
public void deleteObserver(Observer observer){
this.observerList.remove(observer);
}
//通知所有的观察者
public void notifyObservers(String context){
for(Observer observer:observerList){
observer.update(context);
}
}
//韩非子要吃饭了
public void haveBreakfast(){
System.out.println("韩非子:开始吃饭了...");
//通知所有的观察者
this.notifyObservers("韩非子在吃饭");
}
//韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
public void haveFun(){
System.out.println("韩非子:开始娱乐了...");
this.notifyObservers("韩非子在娱乐");
}
}
再看观察者:
public interface Observer {
//一发现别人有动静,自己也要行动起来
public void update(String context);
}
然后观察者来实现这个接口即可,有多少个观察者都行。
在主类中:
//三个观察者产生出来
Observer liSi = new LiSi();
Observer wangSi = new WangSi();
Observer liuSi = new LiuSi();
//定义出韩非子
HanFeiZi hanFeiZi = new HanFeiZi();
//我们后人根据历史,描述这个场景,有三个人在观察韩非子
hanFeiZi.addObserver(liSi);
hanFeiZi.addObserver(wangSi);
hanFeiZi.addObserver(liuSi);
//然后这里我们看看韩非子在干什么
hanFeiZi.haveBreakfast();
这样就完成了监视的过程。当然JDK 中提供了:
java.util.Observable 实现类和 java.util.Observer 接口,于是乎上面写的那个例子中的
Observable 接口可以改换成 java.util.Observale 实现类了。
下面看一下通用类图:
总结:
使用观察者模式要考虑的问题:
(1)广播链的问题。如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表 A 上写了一个触发器,内容是一个字段更新后更新表 B 的一条数据,而表 B 上也有个触发器,要更新表 C,表 C 也有触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次) ,这还是比较好控制的;
(2)异步处理问题。被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步。
举一个应用场景:
比如你到 ATM 机器上取钱,多次输错密码,卡就会被 ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一摄像头连续快拍,第二,通知监控系统,吞卡发生;
第三,初始化 ATM 机屏幕,返回最初状态,你不能因为就吞了一张卡,整个 ATM 都不能用了吧,一般前两
个动作都是通过观察者模式来完成的。