设计模式-观察者模式

观察者模式例子:秦王嬴政让李斯监控韩非子
看看李斯怎么监控韩非子,先看类图:
这里写图片描述
我们来看程序的实现,先看我们的主角韩非子的接口(类似于韩非子这样的人,被观察者角色)

package com.nextvpu.myapplication;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 类似韩非子这样的人,被监控起来了还不知道
 */

public interface IHanFeiZi {
    //韩非子也是人,也要吃早饭的
    public void haveBreakfast();

    //韩非子也要娱乐活动的
    public void haveFun();


}

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

package com.nextvpu.myapplication;

import android.util.Log;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 韩非子,大神级任务
 */

public class HanFeiZi implements IHanFeiZi {

    //韩非子是否在吃饭,作为监控的判断标准
    private boolean isHaveBreakfast = false;

    //韩非子是否在娱乐
    private boolean isHaveFun = false;


    @Override
    public void haveBreakfast() {
        Log.e("xyz"," 韩非子:开始吃早饭了... ");
        this.isHaveBreakfast = true;
    }

    @Override
    public void haveFun() {
        Log.e("xyz"," 韩非子:开始娱乐了... ");
        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 com.nextvpu.myapplication;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 李斯嘛,偷窥狂咯
 */

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

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

package com.nextvpu.myapplication;

import android.util.Log;

/**
 * Created by NEXTVPU on 2018/6/10.
 */

public class LiSi implements ILiSi {
    @Override
    public void update(String context) {
        Log.e("xyz","李斯:观察到韩非子活动,开始向秦老板报告");
        this.reportToQiShiHuang(context);
    }


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

两个重量级的任务都定义出来了,按我们就来看看要怎么监控,先写个监控程序:

package com.nextvpu.myapplication;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 监控程序
 */

public 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()就可以启动一个线程了,我们继续往下看:

  //定义出韩非子和李斯
        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();

        //然后这里我们看看韩非子在干什么
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        hanFeiZi.haveBreakfast();

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        hanFeiZi.haveFun();

结果实现了,但是效率为负。别的不说,使用了一个while(true)这样一个死循环来做监听,你要是用到项目中,你要多少硬件投入进来?并且这个程序根本不是面向对象的程序,这完全是面向过程的,不改不行,怎么修改呢?
我们来想,既然韩非子以吃饭李斯就知道了,那我们为什么不把李斯这个类聚集到韩非子这个类上呢?说改就改,立马动手,我们来看修改后的类图:
这里写图片描述
类图非常简单,就是在HanFeiZi类中引用了ILiSi这个接口,看我们程序代码怎么修改,IHanFeizI接口完全没有修改,我们来看HanFeiZi这个实现类:

package com.nextvpu.myapplication;

import android.util.Log;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 韩非子,大神级任务
 */

public class HanFeiZi implements IHanFeiZi {

    //韩非子是否在吃饭,作为监控的判断标准
    private boolean isHaveBreakfast = false;

    //韩非子是否在娱乐
    private boolean isHaveFun = false;

    //把李斯声明出来
    private ILiSi liSi = new LiSi();


    @Override
    public void haveBreakfast() {
        Log.e("xyz"," 韩非子:开始吃早饭了... ");
//        this.isHaveBreakfast = true;
        this.liSi.update("韩非子在吃饭");
    }

    @Override
    public void haveFun() {
        Log.e("xyz"," 韩非子:开始娱乐了... ");
//        this.isHaveFun = true;
        this.liSi.update("韩非子在娱乐");
    }

    //以下是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;
    }
}

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

 private void Client() {
        //定义出韩非子
        HanFeiZi hanFeiZi = new HanFeiZi();

        //然后这里我们看看韩非子在干什么
        hanFeiZi.haveBreakfast();

        //韩非子娱乐了
        hanFeiZi.haveFun();
    }

运行结果正确,效率也比较高,是不是就大功告成了呢?其实还远远没有,你想想,其他国家的人监察韩非子呢?你在HanFeiZi这个类中定义:
private ILiSi lisi = new LiSi();
一下子就写死了,只有李斯才能观察到韩非子,这是不对的,还有,李斯只观察韩非子吃饭,娱乐吗?政治倾向不关心吗?思维倾向不关心吗?也就是说韩非子的一系列活动都要通知李斯,那可怎么办?按照上面的例子,我们不是要修改疯掉了吗?这和开闭原则严重违背呀,我们程序有问题,怎么修改,来看类图:
这里写图片描述
我们把接口名称修改一下,这样显得更加抽象化,Observable是被观察着,就是类似韩非子这样的人,Observer接口是观察者,类似李斯这样的,同事还有其他国家的比如王斯、刘司等,在Observable接口中有三个比较重要的方法,分别是addObserver增加观察者,deleteObserver删除观察者,notifyObservers通知所有的观察者,这是什么意思呢?我这里有一个信息,一个对象,我可以允许有多个对象来察看,你观察也行,我观察也行,只要是观察者就行,也就是说我的改变或动作执行,会通知其他的对象,看程序会更明白一点,先看Observable接口:

package com.nextvpu.myapplication;

/**
 * Created by NEXTVPU on 2018/6/10.
 */

public interface Observable {
    //增加一个观察者
    public void addObserver(Observer observer);

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

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

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

package com.nextvpu.myapplication;

import android.util.Log;

import java.util.ArrayList;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 韩非子,大神级人物
 */

public class HanFeiZiChange implements Observable {

    //定义个变长数组,存放所有的观察者
    private ArrayList<Observer> observersList = new ArrayList<Observer>();

    //韩非子是否在吃饭,作为监控的判断标准
    private boolean isHaveBreakfast = false;

    //韩非子是否在娱乐
    private boolean isHaveFun = false;

    //把李斯声明出来
    private ILiSi liSi = new LiSi();


    public void haveBreakfast() {
        Log.e("xyz"," 韩非子:开始吃早饭了... ");
//        this.isHaveBreakfast = true;
        this.liSi.update("韩非子在吃饭");
    }

    public void haveFun() {
        Log.e("xyz"," 韩非子:开始娱乐了... ");
//        this.isHaveFun = true;
        this.liSi.update("韩非子在娱乐");
    }

    //以下是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;
    }

    //增加观察者
    @Override
    public void addObserver(Observer observer) {
        this.observersList.add(observer);
    }

    //删除观察者
    @Override
    public void deleteObserver(Observer observer) {
        this.observersList.remove(observer);
    }

    @Override
    public void notifyObservers(String context) {
        for (Observer observer:observersList
        ) {
            observer.update(context);
        }
    }
}

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

package com.nextvpu.myapplication;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

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

然后是李斯和其他国家的观察者:

package com.nextvpu.myapplication;

import android.util.Log;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

public class LiSiChange implements Observer {

    //首先李斯是个观察者,一旦韩非子有活动,他就知道,他就要向老板汇报
    @Override
    public void update(String context) {
        Log.e("xyz","李斯:观察到韩非子活动,开始向老板汇报");
        this.reportToQiShiHuang(context);
        Log.e("xyz","李斯:汇报完毕,秦老板赏黄金");
    }

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

}
package com.nextvpu.myapplication;

import android.util.Log;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

public class LiuSi implements Observer {
    @Override
    public void update(String context) {
        Log.e("xyz"," 王斯:观察到韩非子活动,自己也开始活动了... ");
        this.happy(context);
        Log.e("xyz","王斯:真真的乐死了....\n");
    }

    private  void happy(String context){
        Log.e("xyz"," 王斯:因为 "+context+"高兴的不得了...");
    }
}
package com.nextvpu.myapplication;

import android.util.Log;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

public class WangSi implements Observer {
    @Override
    public void update(String context) {
        Log.e("xyz"," 王斯:观察到韩非子活动,自己也开始活动了... ");
        this.cry(context);
        Log.e("xyz","王斯:真真的哭死了....\n");
    }

    private  void cry(String context){
        Log.e("xyz"," 王斯:因为 "+context+"哇哇大哭...");
    }
}

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

 private void change() {
        //三个观察者产生出来
        Observer liSi = new LiSiChange();
        Observer wangSi = new WangSi();
        Observer liuSi = new LiuSi();

        //定义出韩非子
        HanFeiZiChange hanFeiZi = new HanFeiZiChange();

        //我们后人根据历史,描述这个场景,有三个人在观察韩非子
        hanFeiZi.addObserver(liSi);
        hanFeiZi.addObserver(wangSi);
        hanFeiZi.addObserver(liuSi);

        //然后这里我们看看韩非子在干什么
        hanFeiZi.haveBreakfast();

    }

好了,结果也正确了,也符合开闭原则了,也同时实现类间解耦,想再加观察者?好的,继续实现Observer接口就成了,这时候必须修改Client程序,因为你业务都发生了变化。
你可能已经发现了,HanFeiZi这个实现类应该抽象出一个弗雷,父类完全实现接口,HanFeiZ这个类只实现两个方法havaBreakfast和havaFun就可以了,是的确实是这样的,那先稍等等,我们打开JDK文档看看,其实已经提供了Observable实现类和Observer接口,那我们来看看类图:
这里写图片描述
是不是又简单很多,我们来看一下程序的变更,先看HanFeiZi的实现类:

package com.nextvpu.myapplication;

import android.util.Log;

import java.util.*;

/**
 * Created by NEXTVPU on 2018/6/10.
 * 韩非子,大神级人物
 */

public class HanFeiZiChange extends java.util.Observable {

    public void haveBreakfast() {
        Log.e("xyz"," 韩非子:开始吃早饭了... ");
//        this.isHaveBreakfast = true;
//        this.liSi.update("韩非子在吃饭");
        super.setChanged();
        this.notifyObservers("韩非子在吃饭");
    }

    public void haveFun() {
        Log.e("xyz"," 韩非子:开始娱乐了... ");
//        this.isHaveFun = true;
//        this.liSi.update("韩非子在娱乐");
        super.setChanged();
        this.notifyObservers("韩非子在娱乐");
    }


}

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

package com.nextvpu.myapplication;

import android.util.Log;

import java.util.*;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

public class LiSiChange implements java.util.Observer {


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

    @Override
    public void update(java.util.Observable o, Object arg) {
        Log.e("xyz","李斯:观察到韩非子活动,开始向老板汇报");
        this.reportToQiShiHuang(o.toString());
        Log.e("xyz","李斯:汇报完毕,秦老板赏黄金");
    }
}
package com.nextvpu.myapplication;

import android.util.Log;

import java.util.*;
import java.util.Observable;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

public class WangSi implements java.util.Observer {


    private  void cry(String context){
        Log.e("xyz"," 王斯:因为 "+context+"哇哇大哭...");
    }

    @Override
    public void update(Observable o, Object arg) {
        Log.e("xyz"," 王斯:观察到韩非子活动,自己也开始活动了... ");
        this.cry(o.toString());
        Log.e("xyz","王斯:真真的哭死了....\n");
    }
}
package com.nextvpu.myapplication;

import android.util.Log;

import java.util.*;
import java.util.Observable;

/**
 * Created by NEXTVPU on 2018/6/11.
 */

public class LiuSi implements java.util.Observer {

    private  void happy(String context){
        Log.e("xyz"," 刘斯:因为 "+context+"高兴的不得了...");
    }

    @Override
    public void update(Observable o, Object arg) {
        Log.e("xyz"," 刘斯:观察到韩非子活动,自己也开始活动了... ");
        this.happy(o.toString());
        Log.e("xyz","刘斯:真真的乐死了....\n");
    }
}

以上讲解的就是观察者模式,这个模式的通用类图如下:
这里写图片描述
观察者模式有一个变种叫做发布/订阅模型(Publish/Subscribe)
那观察者模式在什么情况下使用呢?观察者可以实现消息的广播,一个消息可以触发多个事件,这是观察者模式非常重要的功能。使用观察者模式也有两个重要问题要解决:
广播链的问题。一个观察者可以有双重身份,既是观察者也是被观察者,这没什么问题啊,但是链一旦建立,这个逻辑就比较复杂,可维护性非常差。根据经验建议,在一个观察者模式中最多出现一个对象既是观察者也是被观察者,也就是说消息最多传发一次(传递两次),还是比较好处理的;
异步处理问题。被观察者发生动作了,观察者要作出回应,如果观察者比较多,而且处理时间比较长怎么办?那就用异步呗,异步处理就要考虑线程安全和队列的问题,这个大家看看Message Queue,就会有更深的了解。

观察者模式DEMO下载地址:
demo下载

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值