观察者模式例子:秦王嬴政让李斯监控韩非子
看看李斯怎么监控韩非子,先看类图:
我们来看程序的实现,先看我们的主角韩非子的接口(类似于韩非子这样的人,被观察者角色)
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下载