设计模式之观察者设计模式

来源:互联网 发布:江西省网络作家协会 编辑:程序博客网 时间:2024/06/18 06:19

首先介绍我在实际开发过程中使用到的观察者模式:
在写一个Android应用商城的时候,下载某个App时可以在主页点击下载,也可以查看这个App详情页面下载.而无论点击那个下载链接来下载,它们两个下载的进度必须是同步的,这就需要两个观察者来观察下载这一个过程。
首先简单介绍一下观察者设计模式,包括java自带的观察者的接口与被观察者的抽象类.文章后面详细介绍观察者设置模式.
java的util包提供了Observable类与Observer接口。其中继承Observable的为被观察者,实现Observer接口的为观察者:
被观察者的代码:

public class DownLoadManage extends Observable{    @Override    //hasChanged必须重写,返回true表示改变了,才会通知观察者    public boolean hasChanged() {        return  true;    }}

观察者的示例代码:

public class ToSeeDownLoad1 implements Observer {//实现接口的update的方法,当被观察者有动静时调用    @Override    public void update(Observable observable, Object o) {        System.out.println("1看见你的变化");    }}

测试类:

 //观察者与被观察者   DownLoadManage   downLoadManage = new DownLoadManage();   //为被观察者注册观察者    downLoadManage.addObserver(new ToSeeDownLoad1());   downLoadManage.addObserver(new ToSeeDownLoad2());//被观察者通知观察者downLoadManage.notifyObservers();

如上就是观察者模式的简单案例,但这个有个很大的缺陷就是被观察者是继承了Observable抽象类,我们知道java只能单继承。所以java的util自动为我们提供的抽象类使用很局限。所以我们可以通过查看ObServable的源码来自己实现被观察者。

通过查看 源码可以知道,被观察者的ObServable其实内部维护了一个泛型为Observer(观察者的接口)的集合。当增加观察者时就向集合中增加对象,调用notifyObservers()方法时就是遍历集合的每个数据来调用接口中的update的方法。我们仿照这个逻辑来自己定义观察者的接口而改变被观察者继承ObServable的局限性。

被观察者的接口

public interface MyObserverInterface {    //以后观察者实现这个接口    void update();}

被观察者:

public class DownLoadManageForMyObserver {    List<MyObserverInterface> list = new ArrayList<MyObserverInterface>();   //添加观察者    public synchronized void addObServer(MyObserverInterface myObserverInterface){        if(myObserverInterface==null){            throw new NullPointerException();        }else  if (!list.contains(myObserverInterface)) {            list.add(myObserverInterface);        }    }    //通知观察者数据改变了来调用观察者的update方法    public  synchronized void notifyObServers(){        for(MyObserverInterface myObserverInterface : list){            myObserverInterface.update();        }    }}

观察者:

public class MyToSeeDownLoad1 implements MyObserverInterface {    @Override    public void update() {        System.out.println("1 see");    }}

测试类:

  //观察者与被观察者    DownLoadManageForMyObserver    downLoadManageForMyObserver = new DownLoadManageForMyObserver();        downLoadManageForMyObserver.addObServer(new MyToSeeDownLoad1());         downLoadManageForMyObserver.notifyObServers();

经过修改之后,观察者的扩展性也更强了除了update()方法之外,也可以增加其他方法。最后只有在被观察者中调用就行。
现在我们从头到尾学习观察者设计模式:
参考书籍 : 秦小波老师的《设计模式之禅》

案例:
以李斯(观察者)观察韩非子(被观察者)为例来说明:

被观察者接口public interface IHanFeiZi {//韩非子也是人,也要吃早饭的public void haveBreakfast();//韩非之也是人,是人就要娱乐活动public void haveFun();}

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

/*具体的被观察者*/public class HanFeiZi implements IHanFeiZi{//韩非子是否在吃饭,作为监控的判断标准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;}}

通过isHavingBreakfast和isHavingFun这两个布尔型变量来判断韩非子是否在吃饭或者娱乐,韩非
子属于被观察者,那还有观察者李斯,我们来看李斯的接口

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

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

public class LiSi implements ILiSi{//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报public void update(String str){System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");this.reportToQinShiHuang(str);System.out.println("李斯:汇报完毕...\n");}//汇报给秦始皇private void reportToQinShiHuang(String reportContext){System.out.println("李斯:报告,秦老板!韩非子有活动了--->"+reportContext);}}

两个重量级的人物都定义出来了,间谍这个“卑鄙”小人是不是也要登台了,如代码清单:

class Spy extends Thread{private HanFeiZi hanFeiZi;private LiSi liSi;private String type;//通过构造函数传递参数,我要监控的是谁,谁来监控,要监控什么public Spy(HanFeiZi _hanFeiZi,LiSi _liSi,String _type){this.hanFeiZi =_hanFeiZi;this.liSi = _liSi;this.type = _type;}@Overridepublic void run(){while(true){if(this.type.equals("breakfast")){ //监控是否在吃早餐//如果发现韩非子在吃饭,就通知李斯if(this.hanFeiZi.isHavingBreakfast()){this.liSi.update("韩非子在吃饭");//重置状态,继续监控this.hanFeiZi.setHavingBreakfast(false);}}else{//监控是否在娱乐if(this.hanFeiZi.isHavingFun()){this.liSi.update("韩非子在娱乐");this.hanFeiZi.setHavingFun(false);}}}}}

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

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();}}

运行结果如下所示:
韩非子:开始吃饭了…
李斯:观察到韩非子活动,开始向老板汇报了…
李斯:报告,秦老板!韩非子有活动了—>韩非子在吃饭
李斯:汇报完毕
韩非子:开始娱乐了…
李斯:观察到韩非子活动,开始向老板汇报了…
李斯:报告,秦老板!韩非子有活动了—>韩非子在娱乐
李斯:汇报完毕

按照如上的代码可以完成观察的需求。当通过一个while(true)的死循环来完全是不可取的,我们需要优化上述代码:
我们来想,既然韩非子一吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子那个类上呢?

IHanFeiZi接口完全没有修改,我们来看实现类:

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程序的变更:

public class Client {public static void main(String[] args) {//定义出韩非子HanFeiZi hanFeiZi = new HanFeiZi();//然后我们看看韩非子在干什么hanFeiZi.haveBreakfast();//韩非子娱乐了hanFeiZi.haveFun();}}

我们思考一下,修改后的程序运行结果正确,效率也比较高,是不是应该乐呵乐呵了?大功告成了?稍等等,你想在战国争雄的时候,韩非子这么有名望、有实力的人,就只有秦国关心他吗?想想也不可能呀,确实有一大帮的各国类似于李斯这样的人在看着他,监视着他的一举一动,但是看看我们的程序,你在HanFeiZi这个类中定义:

private ILiSi liSi =new LiSi();

这样一来只有李斯才能观察到韩非子,这是不对的,也就是说韩非子的活动只通知了李斯一个人,这不可能;再者说了,李斯只观察韩非子的吃饭、娱乐吗?政治倾向不关心吗?思维倾向不关心吗?杀人放火不关心吗?也就说韩非子的一系列活动都要通知李斯,这可怎么办?要按照上面的例子,我们如何修改?

我们在原来的基础上做了两个修改:
● 增加Observable
实现该接口的都是被观察者,那韩非子是被观察者,他当然也要实现该接口了,同时他
还有与其他庸人相异的事要做,因此他还是要实现IHanFeizi接口。
● 修改ILiSI接口名称为Observer
接口名称修改了一下,这样显得更抽象化,所有实现该接口的都是观察者(类似李斯这
样的)。

Observable是被观察者,就是类似韩非子这样的人,在Observable接口中有三个比较重要的方法,分别是addObserver增加观察者,deleteObserver删除观察者,notifyObservers通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也成,我观察也成,只要是观察者就成,也就是说我的改变或动作执行,会通知其他的对象,看程序会更明白一点,先看Observable接口:

public interface Observable {//增加一个观察者public void addObserver(Observer observer);//删除一个观察者public void deleteObserver(Observer observer);//既然要观察,我发生改变了他也应该有所动作,通知观察者public void notifyObservers(String context);}

被观察者实现类

public class HanFeiZi implements Observable ,IHanFeiZi{//定义个变长数组,存放所有的观察者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("韩非子在娱乐");}}

观察者只是把原有的ILiSi接口修改了一个名字而已,如代码清单22-11所示。

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

具体的观察者1

public class LiSi implements Observer{//首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报public void update(String str){System.out.println("李斯:观察到韩非子活动,开始向老板汇报了...");this.reportToQinShiHuang(str);System.out.println("李斯:汇报完毕...\n");}//汇报给秦始皇private void reportToQinShiHuang(String reportContext){System.out.println("李斯:报告,秦老板!韩非子有活动了-->"+reportContext);}}

具体的观察者1

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+",--所以我悲伤呀!");}}

具体的观察者2

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+",--所以我快乐呀!" );}}

场景类

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程序,因为你的业务都发生了变化。这就是观察者模式。

1 0
原创粉丝点击