第 16 章 观察者模式【Observer Pattern】

以下内容出自:<<24种设计模式介绍与6大设计原则>>

  《孙子兵法》有云:“知彼知己,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆”,那怎么才能知己知彼呢?知己是很容易的,自己的军队嘛,很容易知道,那怎么知彼呢?安插间谍是很好的一个办法,我们今天就来讲一个间谍的故事。
  韩非子大家都应该记得吧,法家的代表人物,主张建立法制社会,实施重罚制度,真是非常有远见呀,看看现在社会在呼吁什么,建立法制化的社会,在2000 多年前就已经提出了。大家可能还不知道,法家还有一个非常重要的代表人物,李斯,对,就是李斯,秦国的丞相,最终被残忍的车裂的那位,李斯和韩非子都是荀子的学生,李斯是师兄,韩非子是师弟,若干年后,李斯成为最强诸侯秦国的上尉,致力于统一全国,于是安插了间谍到各个国家的重要人物的身边,以获取必要的信息,韩非子作为韩国的重量级人物,身边自然没少间谍了,韩非子早饭吃的什么,中午放了几个P,晚上在做什么娱乐,李斯都了如指掌,那可是相隔千里!怎么做到的呢?间谍呀! 好,我们先通过程序把这个过程展现一下,看看李斯是怎么监控韩非子,先看类图:

  看惯了清一色的谈黄色类图,咱换个颜色,写程序是一个艺术创作过程,我的一个同事就曾经把一个类图画成一个小乌龟的形状,超级牛X。这个类图应该是程序员最容易想到得,你要监控,我就给你监控,正确呀,我们来看程序的实现,先看我们的主角韩非子的接口(类似于韩非子这样的人,被观察者角色):

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 类似韩非子这花样的人,被监控起来了还不知道
 */
public interface IHanFeiZi {
    // 韩非子也是人,也要吃早饭的
    public void haveBreakfast();

    // 韩非之也是人,是人就要娱乐活动,至于活动时啥,嘿嘿,不说了
    public void haveFun();
}

然后看韩非子的实现类HanFeiZi.java:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 韩非子,李斯的师弟,韩国的重要人物
 */
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;
    }
}

  其中有两个getter/setter 方法,这个就没有在类图中表示出来,比较简单,通过isHaveBreakfast
和isHaveFun 这两个布尔型变量来判断韩非子是否在吃饭或者娱乐,韩非子是属于被观察者,那还有观察
者李斯,我们来看李斯这类人的接口:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 类似于李斯的这种人,现代嘛叫做偷窥狂
 */
public interface ILiSi {
    // 一发现别人有动静,自己也要行动起来
    public void update(String context);
}

  李斯这类人比较简单,一发现自己观察的对象发生了变化,比如吃饭,娱乐了,自己立刻也要行动起
来,那怎么行动呢?看实现类:

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 */
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);
    }
}

  韩非子是秦始皇非常崇拜的人物,甚至说过见韩非子一面死又何憾!不过,韩非子还真是被秦始皇干掉的,历史呀上演过太多这样的悲剧。这么重要的人物有活动,你李斯敢不向老大汇报?!两个重量级的人物都定义出来了,那我们就来看看要怎么监控,先写个监控程序:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 监控程序
 */
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);
                }
            }
        }
    }
}

  监控程序继承了java.lang.Thread 类,可以同时启动多个线程进行监控,Java 的多线程机制还是比较简单的,继承Thread 类,重写run()方法,然后new SubThread(),再然后subThread.start()就可以启动一个线程了,

我们继续往下看:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 这个Client就是我们,用我们的视角看待这段历史
 */
public class Client {
    public static void main(String[] args) throws InterruptedException {
        // 定义出韩非子和李斯
        LiSi liSi = new LiSi();
        HanFeiZi hanFeiZi = new HanFeiZi();
        // 观察早餐
        Watch watchBreakfast = new Watch(hanFeiZi, liSi, "breakfast");
        // 开始启动线程,监控
        watchBreakfast.start();
        // 观察娱乐情况
        Watch watchFun = new Watch(hanFeiZi, liSi, "fun");
        watchFun.start();
        // 然后这里我们看看韩非子在干什么
        Thread.sleep(1000); // 主线程等待1秒后后再往下执行
        hanFeiZi.haveBreakfast();
        // 韩非子娱乐了
        Thread.sleep(1000);
        hanFeiZi.haveFun();
    }
}

运行结果如下:

韩非子:开始吃饭了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...
韩非子:开始娱乐了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在娱乐
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...

  结果出来,韩非子一吃早饭李斯就知道,韩非子一娱乐李斯也知道,非常正确!结果正确但并不表示
你有成绩,我告诉你:你的成绩是0,甚至是负的,你有没有看到你的CPU 飙升,Eclipse 不响应状态?看
到了?看到了你还不想为什么?!看看上面的程序,别的就不多说了,使用了一个while(true)这样一个死
循环来做监听,你要是用到项目中,你要多少硬件投入进来?你还让不让别人的程序也run 起来?!一台
服务器就跑你这一个程序就完事了,错,绝对的错!
  错误也看到了,我们必须要修改,这个没有办法应用到项目中去呀,而且这个程序根本就不是面向对
象的程序,这完全是面向过程的(我写出这样的程序也不容易呀,安慰一下自己),不改不行,怎么修改呢?
我们来想,既然韩非子一吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子这里类上呢?说
改就改,立马动手,我们来看修改后的类图:

  类图非常简单,就是在HanFeiZi 类中引用了IliSi 这个接口,看我们程序代码怎么修改,IhanFeiZi
接口完全没有修改,我们来看HanFeiZi 这个实现类:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 韩非子,李斯的师弟,韩国的重要人物
 */
public class HanFeiZi implements IHanFeiZi {
    // 把李斯声明出来
    private ILiSi liSi = new LiSi();

    // 韩非子要吃饭了
    public void haveBreakfast() {
        System.out.println("韩非子:开始吃饭了...");
        // 通知李斯
        this.liSi.update("韩非子在吃饭");
    }

    // 韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
    public void haveFun() {
        System.out.println("韩非子:开始娱乐了...");
        this.liSi.update("韩非子在娱乐");
    }
}

  韩非子HanFeiZi 实现类就把接口的两个方法实现就可以了,在每个方法中调用LiSi.update()方法,
完成李斯观察韩非子任务,李斯的接口和实现类都没有任何改变,我们再来看看Client 程序的变更:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 这个Client就是我们,用我们的视角看待这段历史
 */
public class Client {
    public static void main(String[] args) {
        // 定义出韩非子
        HanFeiZi hanFeiZi = new HanFeiZi();
        // 然后这里我们看看韩非子在干什么
        hanFeiZi.haveBreakfast();
        // 韩非子娱乐了
        hanFeiZi.haveFun();
    }
}

李斯都不用在Client 中定义了,非常简单,运行结果如下:

韩非子:开始吃饭了...
李斯:观察到韩非子活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...
韩非子:开始娱乐了...
李斯:观察到韩非子活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在娱乐
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...

  运行结果正确,效率也比较高,是不是应该乐呵乐呵了?大功告成了?稍等等,你想在战国争雄的时候,韩非子这么有名望(法家代表)、有实力(韩国的公子,他老爹参与过争夺韩国王位)的人,就只有秦国一个国家关心他吗?想想也不可能呀,肯定有一大帮的各国的类似李斯这样的人在看着他,监视着一举一动,但是看看我们的程序,在HanFeiZi 这个类中定义:

private ILiSi liSi =new LiSi();

  一下子就敲死了,只有李斯才能观察到韩非子,这是不对的,也就是说韩非子的活动只通知了李斯一个人,这不可能;再者,李斯只观察韩非子的吃饭,娱乐吗?政治倾向不关心吗?思维倾向不关心吗?杀人放火不关心吗?也就说韩非子的一系列活动都要通知李斯,那可怎么办?要按照上面的例子,我们不是要修改疯掉了吗?这和开闭原则严重违背呀,我们的程序有问题,怎么修改,来看类图:

  我们把接口名称修改了一下,这样显得更抽象化,Observable 是被观察者,就是类似韩非子这样的人,Observer 接口是观察者,类似李斯这样的,同时还有其他国家的比如王斯、刘斯等,在Observable 接口中有三个比较重要的方法,分别是addObserver 增加观察者,deleteObserver 删除观察者,notifyObservers通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也成,我观察也成,只要是观察者就成,也就是说我的改变或动作执行,会通知其他的对象,看程序
会更明白一点,先看Observable 接口:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 所有被观察者者,通用接口
 */
public interface Observable {
    // 增加一个观察者
    public void addObserver(Observer observer);

    // 删除一个观察者,——我不想让你看了
    public void deleteObserver(Observer observer);

    // 既然要观察,我发生改变了他也应该用所动作——通知观察者
    public void notifyObservers(String context);
}

这是一个通用的被观察者接口,所有的被观察者都可以实现这个接口。再来看韩非子的实现类:

package cn.mjorcen.observer_pattern;

import java.util.ArrayList;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 韩非子,李斯的师弟,韩国的重要人物
 */
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("韩非子在娱乐");
    }
}

再来看观察者接口Observer.java:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 所有观察者,通用接口
 */
public interface Observer {
    // 一发现别人有动静,自己也要行动起来
    public void update(String context);
}

然后是三个很无耻的观察者,偷窥狂嘛:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 */
public class LiSi implements Observer {
    // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
    public void update(String str) {
        System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
        this.reportToQiShiHuang(str);
        System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
    }

    // 汇报给秦始皇
    private void reportToQiShiHuang(String reportContext) {
        System.out.println("李斯:报告,秦老板!韩非子有活动了--->" + reportContext);
    }
}

李斯是真有其人,以下两个观察者是杜撰出来的:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 王斯,也是观察者,杜撰的人名
 */
public class WangSi implements Observer {
    // 王斯,看到韩非子有活动,自己就受不了
    public void update(String str) {
        System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
        this.cry(str);
        System.out.println("王斯:真真的哭死了...\n");
    }

    // 一看李斯有活动,就哭,痛哭
    private void cry(String context) {
        System.out.println("王斯:因为" + context + ",——所以我悲伤呀!");
    }
}
package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道 杜撰的人名
 */
public class LiuSi implements Observer {
    // 刘斯,观察到韩非子活动后,自己也做一定得事情
    public void update(String str) {
        System.out.println("刘斯:观察到韩非子活动,开始动作了...");
        this.happy(str);
        System.out.println("刘斯:真被乐死了\n");
    }

    // 一看韩非子有变化,他就快乐
    private void happy(String context) {
        System.out.println("刘斯:因为" + context + ",——所以我快乐呀!");
    }
}

所有的历史人物都在场了,那我们来看看这场历史闹剧是如何演绎的:

package cn.mjorcen.observer_pattern;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 这个Client就是我们,用我们的视角看待这段历史
 */
public class Client {
    public static void main(String[] args) {
        // 三个观察者产生出来
        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();
    }
}

运行结果如下:

韩非子:开始吃饭了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...
王斯:观察到韩非子活动,自己也开始活动了...
王斯:因为韩非子在吃饭,——所以我悲伤呀!
王斯:真真的哭死了...
刘斯:观察到韩非子活动,开始动作了...
刘斯:因为韩非子在吃饭,——所以我快乐呀!
刘斯:真被乐死了

  好了,结果也正确了,也符合开闭原则了,也同时实现类间解耦,想再加观察者?好呀,继续实现Observer 接口就成了,这时候必须修改Client 程序,因为你业务都发生了变化。细心的你可能已经发现,HanFeiZi 这个实现类中应该抽象出一个父类,父类完全实现接口,HanFeiZi这个类只实现两个方法haveBreakfast 和haveFun 就可以了,是的,是的,确实是应该这样,那先稍等等,我们打开JDK 的帮助文件看看,查找一下Observable 是不是已经有这个类了? JDK 中提供了:java.util.Observable 实现和java.util.Observer 接口,也就是说我们上面写的那个例子中的Observable 接口可以改换成java.util.Observale 实现类了,

看如下类图:

是不是又简单了很多?那就对了!然后我们看一下我们程序的变更,先看HanFeiZi 的实现类:

package cn.mjorcen.observer_pattern;

import java.util.Observable;

/**
 * @author cbf4Life [email protected]  *         I‘m glad to share my knowledge with you
 *         all. 韩非子,李斯的师弟,韩国的重要人物
 */
public class HanFeiZi extends Observable {
    // 韩非子要吃饭了
    public void haveBreakfast() {
        System.out.println("韩非子:开始吃饭了...");
        // 通知所有的观察者
        super.setChanged();
        super.notifyObservers("韩非子在吃饭");
    }

    // 韩非子开始娱乐了,古代人没啥娱乐,你能想到的就那么多
    public void haveFun() {
        System.out.println("韩非子:开始娱乐了...");
        super.setChanged();
        this.notifyObservers("韩非子在娱乐");
    }
}

  改变的不多,引入了一个java.util.Observable 对象,删除了增加、删除观察者的方法,简单了很多,
那我们再来看观察者的实现类:

package cn.mjorcen.observer_pattern;

import java.util.Observable;
import java.util.Observer;

/**
 * @author cbf4Life [email protected]
 *
 *         I‘m glad to share my knowledge with you all.
 *         李斯这个人,是个观察者,只要韩非子一有动静,这边就知道
 */
public class LiSi implements Observer {
    // 首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
    public void update(Observable observable, Object obj) {
        System.out.println("李斯:观察到李斯活动,开始向老板汇报了...");
        this.reportToQiShiHuang(obj.toString());
        System.out.println("李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...\n");
    }

    // 汇报给秦始皇
    private void reportToQiShiHuang(String reportContext) {
        System.out.println("李斯:报告,秦老板!韩非子有活动了--->" + reportContext);
    }
}

  就改变了黄色的部分,应该java.util.Observer 接口要求update 传递过来两个变量,Observable 这个
变量我们没用到,就不处理了。其他两个观察者实现类也是相同的改动,如下代码:

package cn.mjorcen.observer_pattern;

import java.util.Observable;
import java.util.Observer;

/**
 * @author cbf4Life [email protected]
 *
 *         I‘m glad to share my knowledge with you all.
 *
 *         王斯,也是观察者,杜撰的人名
 */
public class WangSi implements Observer {
    // 王斯,看到韩非子有活动,自己就受不了
    public void update(Observable observable, Object obj) {
        System.out.println("王斯:观察到韩非子活动,自己也开始活动了...");
        this.cry(obj.toString());
        System.out.println("王斯:真真的哭死了...\n");
    }

    // 一看李斯有活动,就哭,痛哭
    private void cry(String context) {
        System.out.println("王斯:因为" + context + ",——所以我悲伤呀!");
    }
}
package cn.mjorcen.observer_pattern;

import java.util.Observable;
import java.util.Observer;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 刘斯这个人,是个观察者,只要韩非子一有动静,这边就知道 杜撰的人名
 */
public class LiuSi implements Observer {
    // 刘斯,观察到韩非子活动后,自己也做一定得事情
    public void update(Observable observable, Object obj) {
        System.out.println("刘斯:观察到韩非子活动,开始动作了...");
        this.happy(obj.toString());
        System.out.println("刘斯:真被乐死了\n");
    }

    // 一看韩非子有变化,他就快乐
    private void happy(String context) {
        System.out.println("刘斯:因为" + context + ",——所以我快乐呀!");
    }
}

然后再来看Client 程序:

package cn.mjorcen.observer_pattern;

import java.util.Observer;

/**
 * @author cbf4Life [email protected] I‘m glad to share my knowledge with you
 *         all. 这个Client就是我们,用我们的视角看待这段历史
 */
public class Client {
    public static void main(String[] args) {
        // 三个观察者产生出来
        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();
    }
}

程序体内没有任何变更,只是引入了一个接口而已,运行结果如下:

韩非子:开始吃饭了...
刘斯:观察到韩非子活动,开始动作了...
刘斯:因为韩非子在吃饭,——所以我快乐呀!
刘斯:真被乐死了
王斯:观察到韩非子活动,自己也开始活动了...
王斯:因为韩非子在吃饭,——所以我悲伤呀!
王斯:真真的哭死了...
李斯:观察到李斯活动,开始向老板汇报了...
李斯:报告,秦老板!韩非子有活动了--->韩非子在吃饭
李斯:汇报完毕,秦老板赏给他两个萝卜吃吃...

运行结果一样,只是通知的先后顺序不同而已,程序已经简约到极致了。
以上讲解的就是观察者模式,这个模式的通用类图如下:

  观察者模式在实际项目的应用中非常常见,比如你到ATM 机器上取钱,多次输错密码,卡就会被ATM吞掉,吞卡动作发生的时候,会触发哪些事件呢?第一摄像头连续快拍,第二,通知监控系统,吞卡发生;第三,初始化ATM 机屏幕,返回最初状态,你不能因为就吞了一张卡,整个ATM 都不能用了吧,一般前两个动作都是通过观察者模式来完成的。
  观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe),如果你做过EJB(EnterpriseJavaBean)的开发,这个你绝对不会陌生。EJB2 是个折腾死人不偿命的玩意儿,写个Bean 要实现,还要继承,再加上那一堆的配置文件,小项目还凑活,你要知道用EJB 开发的基本上都不是小项目,到最后是每个项目成员都在骂EJB 这个忽悠人的东西;但是EJB3 是个非常优秀的框架,还是算比较轻量级,写个Bean只要加个Annotaion 就成了,配置文件减少了,而且也引入了依赖注入的概念,虽然只是EJB2 的翻版,但是毕竟还是前进了一步,不知道以后EJB 的路会怎么样。

  在EJB 中有三个类型的Bean: Session Bean、EntityBean 和MessageDriven Bean,我们这里来说一下MessageDriven Bean(一般简称为MDB),消息驱动Bean,

  消息的发布者(Provider)发布一个消息,也就是一个消息驱动Bean,通过EJB 容器(一般是Message Queue消息队列)通知订阅者做出回应,从原理上看很简单,就是观察者模式的升级版。

那观察者模式在什么情况下使用呢?观察者可以实现消息的广播,一个消息可以触发多个事件,这是

观察者模式非常重要的功能。使用观察者模式也有两个重点问题要解决:

广播链的问题 

  如果你做过数据库的触发器,你就应该知道有一个触发器链的问题,比如表A 上写了一个触发器,内容是一个字段更新后更新表B 的一条数据,而表B 上也有个触发器,要更新表C,表C 也有触发器…,完蛋了,这个数据库基本上就毁掉了!我们的观察者模式也是一样的问题,一个观察者可以有双重身份,即使观察者,也是被观察者,这没什么问题呀,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差,根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多转发一次(传递两次),这还是比较好控制的;
异步处理问题

  这个EJB 是一个非常好的例子,被观察者发生动作了,观察者要做出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家有时间看看Message Queue,就会有更深的了解。我们在来回顾一下我们写的程序,观察者增加了,我就必须修改业务逻辑Client 程序,这个是必须得吗?回顾一下我们以前讲到工厂方法模式的时候用到了ClassUtils 这个类,其中有一个方法就是根据接口查找到所有的实现类,问题解决了吧!我可以查找到所有的观察者,然后全部加进来,以后要是新增加观察者也没有问题呀,程序那真是一点都不用改了!

第 16 章 观察者模式【Observer Pattern】

时间: 2024-10-14 12:12:36

第 16 章 观察者模式【Observer Pattern】的相关文章

第16章 观察者模式(Oberver Pattern)

原文  第16章 观察者模式(Oberver Pattern) 观察者模式  概述:   在软件构建过程中,我们需要为某些对象建立一种"通知依赖关系" --一个对象(目标对象)的状态发生改变,所有的依赖对象(观察者对象)都将得到通知.如果这样的依赖关系过于紧密,将使软件不能很好地抵御变化.使用面向对象技术,可以将这种依赖关系弱化,并形成一种稳定的依赖关系.从而实现软件体系结构的松耦合. 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时, 所有依赖于它的对象都得到通知并被自动

设计模式 - 观察者模式(Observer Pattern) 详解

观察者模式(Observer Pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权所有, 禁止转载, 如有转载, 请站内联系. 观察者模式(Observer Pattern): 定义了对象之间的一对多的依赖, 这样一来, 当一个对象改变状态时, 它的所有依赖者都会收到通知并自动更新. 使用方法: 1. 首先新建主题(subject)接口, 负责注册(register)\删除(remove

设计模式 - 观察者模式(Observer Pattern) Java内置 使用方法

观察者模式(Observer Pattern) Java内置 使用方法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26601659 观察者模式(observer pattern)详解, 参见: http://blog.csdn.net/caroline_wendy/article/details/26583157 Java内置的观察者模式, 是通过继承父类, 实现观察者模式的几个主要函数: Observerable(可被观

Java设计模式之观察者模式(Observer Pattern)

Observer Pattern 是一种常用的设计模式,它是一种事件监听模型.该模式有两个角色,一个是Subject, 另一个是Observer.Subject 保存有多个Observer的引用,一旦特定的事件发生,Subject会通知它所有的Observer,Observer得到该通知后执行相关程序逻辑.其中,Observer只有先向Subject注册后才能被Subject知晓.这就像订报纸,只有我们向出版社提出订报的申请,出版社才会把我们列入订阅者名单,然后每当新报纸印好时,出版社会通知订阅

设计模式 - 观察者模式(Observer Pattern) 详细说明

观察者模式(Observer Pattern) 详细说明 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26583157 版权全部, 禁止转载, 如有转载, 请站内联系. 观察者模式(Observer Pattern): 定义了对象之间的一对多的依赖, 这样一来, 当一个对象改变状态时, 它的全部依赖者都会收到通知并自己主动更新. 用法: 1. 首先新建主题(subject)接口, 负责注冊(register)\删除(remo

jQuery中的观察者模式(Observer Pattern)

在jQuery中,on方法可以为元素绑定事件,trigger方法可以手动触发事件,围绕这2个方法,我们来体验jQuery中的观察者模式(Observer Pattern). ■ on方法绑定内置事件,自然触发 比如,我们给页面的body元素绑定一个click事件,这样写. <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title&

设计模式之二:观察者模式(Observer Pattern)

先看下观察者模式的定义: The Observer Pattern defines a one-to-many denpendency between objects so that when one object changes state, all of its dependents are notified and updated automatically.:观察者模式定义了对象间一对多依赖关系,使得当一个对象改变状态,则所有依赖于它的对象都会得到通知并被自动更新. 观察者模式又叫发布-

【设计模式】观察者模式 Observer Pattern

定义:观察者模式定义了对象之间的一对多依赖.当“主题”(Object)状态改变事,所有依赖它的“观察者”(Observer)都会受到通知并自动更新.主题支持观察者订阅和退订. 观察者模式提供了一种对象设计,让主题和观察者之间松耦合.改变主题或观察者一方不会影响另一方.因为两者是松耦合的. 参考: 设计模式学习笔记--Observer Pattern观察者模式

读书笔记_java设计模式深入研究 第五章 观察者模式 Observer

1,观察者模式适合解决多种对象跟踪一个对象数据变化的程序结构问题,一个称作"主题"的对象和若干个称作"观察者"的对象.在主题对象更新后会通知所有的观察者,使他们自动更新自己. 2,观察者UML类图: 3,角色解释: -1,抽象观察者(IObserver):为所有具体观察者定义接口,在得到主题通知的时候,更新观察者自身数据. -2,抽象主题(ISubject):使用数组引用维护一组观察者对象,可以增加和删除观察者,同时同志观察者自身的改变. -3,观察者(Observ