设计模式之观察者模式

来源:互联网 发布:linux下卸载oracle11g 编辑:程序博客网 时间:2024/05/01 18:52

一堆男生观察美眉的一举一动,一颦一笑

大家学生时代都追过心仪的女生吧,那我们今天从追女生来聊聊观察者模式。
当年我们班的校花叫“晓丽”,那个美啊。对,就是用闭月羞花,沉鱼落雁来形容。我们班的所有男生都想追她(自然包括我),晓丽的一举一动我们都细心的观察,那叫一个美啊,好先说到这,观察晓丽这个动作,用代码实现一下,先看类图:

晓丽和她的追求者

仅有这二个对象是不够的,我们要解决追求者是怎样监视晓丽的,创建一个后台的线程一直处于运行状态,一旦发现晓丽吃饭或者娱乐就触发事件,那我们就安排一个间谍在晓丽身边(这个间谍就是我买通的晓丽的舍友),观察晓丽的生活起居,并上报给我,然后我就触发update()事件。改一下类图如下:
通过后台线程去监视

代码如下:

public interface IXiaoLi {    //晓丽吃早饭了    public void haveBreakfast();    //晓丽娱乐了    public void haveFun();}

对接口进行扩充,增加二个状态(是否吃饭和娱乐), 以方便监控:

public class XiaoLi implements IXiaoLi {    // 晓丽是否在吃饭,作为监控的判断标准    private boolean isHavingBreakfast = false;    // 晓丽是否在娱乐    private boolean isHavingFun = false;    // 晓丽要吃饭了    public void haveBreakfast() {        System.out.println("晓丽:开始吃饭了...,姿势好美...");        this.isHavingBreakfast = true;    }    // 晓丽开始娱乐了    public void haveFun() {        System.out.println("晓丽:开始娱乐了...,姿势好美...");        this.isHavingFun = true;    }    // 以下是bean的基本方法,getter/setter,不多说    public boolean isHavingBreakfast() {        return isHavingBreakfast;    }    public void setHavingBreakfast(boolean isHavingBreakfast) {        this.isHavingBreakfast = isHavingBreakfast;    }    public boolean isHavingFun() {        return isHavingFun;    }    public void setHavingFun(boolean isHavingFun) {        this.isHavingFun = isHavingFun;    }}

下面的抽象的观察者:

public interface ILover{    //一发现别人有动静,自己也要行动起来    public void update(String context);}

观察者的具体实现:

public class Lover implements ILover {    // 首先追求者是个观察者,一旦晓丽有活动,他就知道了    public void update(String str) {        System.out.println("追求者:"+str+",我好高兴!");    }}

两个重量级的人物都定义出来了,我们的间谍是不是也要登台了:

public class Spy extends Thread {    private XiaoLi xiaoLi;    private Lover lover;    private String type;    // 通过构造函数传递参数,我要监控的是谁,谁来监控,要监控什么    public Spy(XiaoLi xiaoLi, Lover lover, String _type) {        this.xiaoLi = xiaoLi;        this.lover = lover;        this.type = _type;    }    @Override    public void run() {        while (true) {            if (this.type.equals("breakfast")) { // 监控是否在吃早餐                // 如果发现晓丽在吃饭                if (this.xiaoLi.isHavingBreakfast()) {                    this.lover.update("晓丽在吃饭");                    // 重置状态,继续监控                    this.xiaoLi.setHavingBreakfast(false);                }            } else {// 监控是否在娱乐                if (this.xiaoLi.isHavingFun()) {                    this.lover.update("晓丽在娱乐");                    this.xiaoLi.setHavingFun(false);                }            }        }    }}   

监控程序继承了java.lang.Thread类,可以同时启动多个线程进行监控,Java的多线程机制还是比较简单的,继承Thread类,重写run()方法,然后new SubThread(),再然后 subThread.start()就可以启动一个线程了。我们建立一个场景类来回顾一下这段监控:

public class Client {    public static void main(String[] args) throws InterruptedException {        // 定义出晓丽和追求者        Lover lover = new Lover();        XiaoLi xiaoLi = new XiaoLi();        // 观察早餐        Spy SpyBreakfast = new Spy(xiaoLi, lover, "breakfast");        // 开始启动线程,监控        SpyBreakfast.start();        // 观察娱乐情况        Spy SpyFun = new Spy(xiaoLi, lover, "fun");        SpyFun.start();        // 然后我们看看晓丽在干什么        Thread.sleep(1000); // 主线程等待1秒后后再往下执行        xiaoLi.haveBreakfast();        // 晓丽娱乐了        Thread.sleep(1000);        xiaoLi.haveFun();    }       }

运行结果如下:

晓丽:开始吃饭了...,姿势好美...追求者:晓丽在吃饭,我好高兴!晓丽:开始娱乐了...,姿势好美...追求者:晓丽在娱乐,我好高兴!

结果出来,晓丽一吃早饭追求者就知道,晓丽一娱乐追求者也知道,非常正确!
结果正确但并不表示你有成绩,我告诉你:你的成绩是0,甚至是负的!你有没有看到你的CPU飙升,Eclipse直接卡的不动了?反正我的eclipse直接卡的动不了了!!想想这是为什么,看看上面的程序,别的就不多说了,使用了一个死循环while(true)来做监听,要是用到项目中,你要多少硬件投入进来? 你还让不让别人的程序运行了?!一台服务器就跑你这一个程序就完事!

这个程序根本就不是面 向对象的程序,这完全是面向过程的,不改不行,怎么修改呢?我们来想,既然晓丽一吃饭追求者就知道了,那我们为什么不把追求者这个类聚集到晓丽那个类上呢?说改就改,立马动手,我们来看修改后的类图:

晓丽类(被观察者):

public class XiaoLi implements IXiaoLi{    private Lover lover  = new Lover();    //晓丽要吃饭了    public void haveBreakfast(){         System.out.println("晓丽:开始吃饭了...");        //通知追求者        this.lover.update("晓丽在吃饭");    }    //晓丽开始娱乐了    public void haveFun(){         System.out.println("晓丽:开始娱乐了...");          this.lover.update("晓丽在娱乐");    }}

追求者类就不用再赘述了,运行结果相同,非常简单。
大家再想一想:
晓丽的追求者起至我一个,人数多的都能组成一个连了,那怎么办?
我们在代码中private Lover lover = new Lover();这表明只有我一个人在观察,这是不对的,这么一个大美女怎么可能只有我一个人观察呢?

我们做一下修改:
1. 增加Observable
2. 修改ILover接口名称为Observer
代码如下:

public interface Observable {    public void addObserver(Observer observer);    public void delObserver(Observer observer);    public void notifyObserver(String content);}

被观察者类(晓丽类):

public class XiaoLi implements IXiaoLi,Observable {    private ArrayList<Observer> observerList = new ArrayList<Observer>();    // 晓丽要吃饭了    public void haveBreakfast() {        System.out.println("晓丽:开始吃饭了...");        this.notifyObserver("晓丽:开始吃饭了...,姿势好美...");    }    // 晓丽开始娱乐了    public void haveFun() {        System.out.println("晓丽:开始娱乐了...");        this.notifyObserver("晓丽:开始娱乐了...,姿势好美...");    }    @Override    public void addObserver(Observer observer) {        this.observerList.add(observer);    }    @Override    public void delObserver(Observer observer) {        this.observerList.remove(observer);    }    @Override    public void notifyObserver(String content) {        for (Observer observer : observerList) {            observer.update(content);        }    }}

观察者接口:

public interface Observer {    public void update(String context);}

然后三个猥琐的偷窥者(张三,李四,王五):
public class ZhanSanLover implements Observer {
// 首先追求者是个观察者,一旦晓丽有活动,他就知道了
public void update(String str) {
System.out.println(“张三:”+str+”,我好高兴!”);
}
}
public class LiSiLover implements Observer {
// 首先追求者是个观察者,一旦晓丽有活动,他就知道了
public void update(String str) {
System.out.println(“李四:”+str+”,我好高兴!”);
}
}
public class WangWuLover implements Observer {
// 首先追求者是个观察者,一旦晓丽有活动,他就知道了
public void update(String str) {
System.out.println(“王五:”+str+”,我好高兴!”);
}
}

所有的人物都已经登场了,那么我们来看看场景如何演绎:
场景类:

public static void main(String[] args) throws InterruptedException {    // 定义出晓丽和追求者    ZhangSanLover zhangSanLover = new ZhangSanLover();    LiSiLover liSiLover = new LiSiLover();    WangWuLover wangWuLover = new WangWuLover();    XiaoLi xiaoLi = new XiaoLi();    xiaoLi.addObserver(zhangSanLover);    xiaoLi.addObserver(liSiLover);    xiaoLi.addObserver(wangWuLover);    xiaoLi.haveBreakfast();    xiaoLi.haveFun();}

运行结果如下:
晓丽:我开始吃饭了…
张三:我看到晓丽开始吃饭了…,姿势好美…,我好高兴!
李四:我看到晓丽开始吃饭了…,姿势好美…,我好高兴!
王五:我看到晓丽开始吃饭了…,姿势好美…,我好高兴!
晓丽:我开始娱乐了…
张三:我看到晓丽开始娱乐了…,姿势好美…,我好高兴!
李四:我看到晓丽开始娱乐了…,姿势好美…,我好高兴!
王五:我看到晓丽开始娱乐了…,姿势好美…,我好高兴!

好了,符合开闭原则,如过再想添加观察者,继承observer类就好了,这就是观察者模式

观察者模式的缺点如下:
1.一个被观察者和多个观察者导致调试非常麻烦
2.java中消息的通知是循序执行,一旦有一个观察者卡壳,会影响整体的执行效率,所以我们建议用异步的方式。

二用java自带的观察者模式

上面的介绍只是让大家知道观察都模式的原理,别光知道用不知道它的原理,其实java早就给我们封装好了,下面我们来看一下:
我们引入下面二个类:
java.util.Observable;
java.util.Observer;

public class XiaoLi extends Observable implements IXiaoLi {    // 晓丽要吃饭了    public void haveBreakfast() {        System.out.println("晓丽:我开始吃饭了...");        super.setChanged();        super.notifyObservers("我看到晓丽开始吃饭了...,姿势好美...");    }    // 晓丽开始娱乐了    public void haveFun() {        System.out.println("晓丽:我开始娱乐了...");        super.setChanged();        super.notifyObservers("我看到晓丽开始娱乐了...,姿势好美...");    }}public class ZhangSanLover implements Observer {    // 首先追求者是个观察者,一旦晓丽有活动,他就知道了    @Override    public void update(Observable o, Object arg) {        System.out.println("张三:"+arg.toString()+",我好高兴!");    }}

结尾

好了就讲到这里吧希望对大家有所帮助!

0 0