高手眼中的观察者模式和新手有什么不一样

文章目录

  • 基础部分
    • 一、观察者模式的基本经典结构
    • 二、入门小demo
      • 经典小demo1:
      • 大话设计模式中看门放哨小案例
    • 三、经典观察者模式的两种使用方式: 推和拉
  • 高级部分(应用场景)
    • 一、 如何让观察者区别对待
    • 二、如何不依赖抽象观察者,也能实现观察者模式:
      • 1.使用java自带的观察者模式的例子
      • 2.反射委托实现上面第2个例子,学生根据老师到来,做出不同反应的例子。
      • 3.swing组件监听的例子
      • 4.spring中listener实现观察者模式的例子
      • 5.springboot中集成谷歌的Guava,通过eventBus实现订阅发布功能
    • 三、观察者模式的本质

观察者模式,是使用很多的一种模式,初次了解,只是根据入门demo写个例子,但是不知道用在哪,怎么用,很教科书。
个人很喜欢比较实战的的博客或者资料。
最近又恶补了一把,感觉有点小收获,记录下。

基础部分

一、观察者模式的基本经典结构

高手眼中的观察者模式和新手有什么不一样_第1张图片
高手眼中的观察者模式和新手有什么不一样_第2张图片

二、入门小demo

经典小demo1:

高手眼中的观察者模式和新手有什么不一样_第3张图片
Observer

/**
 * 观察者接口,定义一个更新的接口给那些在目标发生改变的时候被通知的对象
 */
public interface Observer {
    /**
     * 更新的接口
     */
    public void update(Subject subject);
}

ConcreteObserver

//具体观察者对象,实现更新的方法,使用自身的状态和
public class ConcreteObserver implements Observer {

    @Override
    public void update(Subject subject) {
        //具体的实现
        //这里可能需要更新观察者的状态,使其与目标的状态保持一致
        String message = ((ConcreteSubject) subject).getSubjectState();
        System.out.println("收到一通知: 获取到的状态是: " + message);
    }

}

Subject

/**
 * 目标对象,它知道观察它的观察者,并提供注册和删除观察者的接口
 */
public class Subject {
    /**
     * 用来保存注册的观察者对象
     */
    private List<Observer> observers = new ArrayList<>();
    /**
     * 注册观察者对象
     */
    public void attach(Observer observer){
        observers.add(observer);
    }
    /**
     * 删除观察者对象
     */
    public void detach(Observer observer){
        observers.remove(observer);
    }
    /**
     * 通知所有注册的观察者对象
     */
    protected void notifyObservers(){
        for (Observer observer: observers){
            observer.update(this);
        }
    }

}

ConcreteSubject

/**
 * 具体的目标对象,负责吧有关状态存入到相应的观察者对象
 * 并在自己状态
 */
public class ConcreteSubject extends Subject {
    /**
     * 目标对象的状态
     */
    private String subjectState;

    public String getSubjectState() {
        return subjectState;
    }

    public void setSubjectState(String subjectState) {
        this.subjectState = subjectState;
        this.notifyObservers();
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //观察者
        Observer concreteObserver1 = new ConcreteObserver();
        Observer concreteObserver2 = new ConcreteObserver();
        //目标对象 即被观察者 目标可以有多个,此demo通过 state区分
        ConcreteSubject subject1 = new ConcreteSubject();
        //注册观察者
        subject1.attach(concreteObserver1);
        subject1.attach(concreteObserver2);
//        ConcreteSubject subject2 = new ConcreteSubject();
//        //注册观察者
//        subject2.attach(concreteObserver1);
//        subject2.attach(concreteObserver2);
        //发出通知
        subject1.setSubjectState("通知1:已经下发了");
//        System.out.println("===换一个主题======");
//        subject2.setSubjectState("通知2:已经下发了");

    }
}

运行结果: 主题发一个消息,观察者都能收到:
高手眼中的观察者模式和新手有什么不一样_第4张图片

大话设计模式中看门放哨小案例

高手眼中的观察者模式和新手有什么不一样_第5张图片
Subject

public interface Subject {
    
	/**
	 * 添加观察者
	 * @param observer
	 */
	void addObserver(Observer observer);
	/**
	 * 移除指定的观察者
	 * @param observer
	 */
	void removeObserver(Observer observer);
	/**
	 * 移除所有的观察者
	 */
	void removeAll();
	
	/**
	 * data 是要通知给观察者的数据
	 * 因为Object是所有类的父类,可以使用多态,当然 你也可以使用 泛型
	 * @param data
	 */
	void notifyAllObserver(Object data);
	
    /**
     * 单独 通知某一个观察者
     * @param observer
     * @param data
     *  data 是要通知给观察者的数据
	 * 因为Object是所有类的父类,可以使用多态,当然 你也可以使用 泛型
     */
	void notify(Observer observer,Object data);

}

ConcreteSubject


/**
 * 具体的主题对象 
 * 这里就不实现线程安全的功能了,
 * 有兴趣的话可以参考java.util报下的Observable
 * @author xujun
 *
 */

public class ConcreteSubject implements Subject {

	List<Observer> mList = new ArrayList<>();

	@Override
	public void addObserver(Observer observer) {
		// 确保相同的观察者只含有一个
		if (observer == null) {
			throw new NullPointerException("observer == null");
		}
		if (!mList.contains(observer)) {
			mList.add(observer);
		}
	}

	@Override
	public void removeObserver(Observer observer) {
		mList.remove(observer);
	}

	@Override
	public void removeAll() {
       mList.clear();
	}

	@Override
	public void notifyAllObserver(Object data) {
		for (Observer observer : mList) {
			observer.update(data);
		}
	}

	@Override
	public void notify(Observer observer, Object data) {
		if(observer!=null){
			observer.update(data);
		}
	}

}

Observer

/**
 * 观察者接口
 * @author Administrator
 *
 */
public interface Observer {
	
	/**
	 * 
	 * @param data    被观察者传递给观察者的 数据
	 */
	void update(Object data);
	

}

CartoonObserver

public class CartoonObserver implements Observer {

	@Override
	public void update(Object data) {
		System.out.println( " 我是"+this.getClass().
				getSimpleName()+",  "+data+"别看漫画了");	
	}

}

NBAObserver

public class NBAObserver implements Observer {
	public class CartoonObserver implements Observer {

		@Override
		public void update(Object data) {
			System.out.println( " 我是"+this.getClass().getSimpleName()+",  "+data+"别看漫画了");
		}
	}

    @Override
    public void update(Object data) {
        System.out.println(" 我是" + this.getClass().getSimpleName() + ",  " + data + "别看NBA了");
    }

}

TestObserver

public class TestObserver {

    public static void main(String[] args) {
        //主题
        ConcreteSubject concreteSubject = new ConcreteSubject();
        //观察者
        CartoonObserver cartoonObserver = new CartoonObserver();
        NBAObserver nbaObserver = new NBAObserver();
        //添加观察者
        concreteSubject.addObserver(cartoonObserver);
        concreteSubject.addObserver(nbaObserver);
       //发布消息通知
        concreteSubject.notifyAllObserver("老师来了");
    }
}

运行结果: 只要放哨的一发通知,观察者就收到了。
在这里插入图片描述

三、经典观察者模式的两种使用方式: 推和拉

观察者模式使用时,其实分2个阶段:

  • 准备阶段,维护目标和观察者关系的阶段

  • 实际运行阶段,也就是目标发生变化,引起观察者做出发应的阶段

这里说的使用方式,针对的是实际运行阶段。获取目标确切数据发起者的问题。

  • 推模型
    目标对象主动向观察者推送目标的详细信息,不管观察者是否需要,这些数据,是目标对象定义,相当于广播给观察者。刚才的例子中,第2个就是推模型。
  • 拉模型
    目标对象在通知观察者的时候,只是把自己的引用给观察者,观察者根据需要,使用引用获取。刚才的例子中,第一个例子就是拉模型。目标对象吧this传递给观察者。

开发中如果数据确定,可以用推模型,如果观察者要得到的数据不固定,建议用拉模型,更加灵活,扩展性强。总之,还是拉模型好。


新手一般只学到上面。下面进入入深入思考的部分:


高级部分(应用场景)

一、 如何让观察者区别对待

上面的demo,均是目前通知观察者的时候全部都通知,根据不同的情况来让不同的观察者处理操作,如何设计呢?

思路:2种,
一种是目标可以全部通知,但是观察者不做任何操作,
另一种就是在目标里面进行判断,直接不通知了,
这里推荐第二种,可以统一逻辑控制,并进行观察者的统一分派,有利于业务控制和今后的扩展。

来个例子: 水质污染,根据污染情况分别通知检测员,预警人员,检测部门领导。
代如下:
高手眼中的观察者模式和新手有什么不一样_第6张图片

WaterQualitySubject - 水质监测的目标对象

/**
 * 定义水质监测的目标对象
 */
public abstract class WaterQualitySubject {
    /**
     * 用来保存注册
     */
    protected List<WatcherObserver> observers = new ArrayList<>();
    /**
     * 注册观察者对象
     */
    public void attach(WatcherObserver observer){
        observers.add(observer);
    }
    /**
     * 删除观察者对象
     */
    public void detach(WatcherObserver observer){
        observers.remove(observer);
    }
    /**
     * 通知相应的观察者对象
     */
    public abstract void notifyWathers();
    /**
     * 获取水质污染的级别
     */
    public abstract int getPolluteLevel();

}

WaterQuality - 具体的水质监测对象

/**
 * 具体的水质监测对象
 */
public class WaterQuality extends WaterQualitySubject {
    /**
     * 污染的级别,0表示正常,1表示轻度污染,2表示中度污染,3表示高度污染
     */
    private int polluteLevel = 0;

    /**
     * 获取水质污染的级别
     */
    @Override
    public int getPolluteLevel() {
        return polluteLevel;
    }

    public void setPolluteLevel(int polluteLevel) {
        this.polluteLevel = polluteLevel;
        this.notifyWathers();
    }

    /**
     * 通知相应的观察者对象
     */
    @Override
    public void notifyWathers() {
        //循环所在注册的观察者
        for (WatcherObserver watcher: observers){
            //开始根据污染级别判断是否需要通知,由这里总控
            if (this.polluteLevel >=0 ){
                //通知监测员做记录
                if (("监测人员").equals(watcher.getJob())){
                    watcher.update(this);
                }
            }
            if (this.polluteLevel >=1 ){
                //通知预警人员
                if (("预警人员").equals(watcher.getJob())){
                    watcher.update(this);
                }
            }
            if (this.polluteLevel >=2 ){
                //通知监测员部门领导
                if (("监测部门领导").equals(watcher.getJob())){
                    watcher.update(this);
                }
            }

        }
    }

}

WatcherObserver

public interface WatcherObserver {
    /**
     * 被通知的方法
     * @param subject
     */
    public void update(WaterQualitySubject subject);
    /**
     * 设置观察人员的职务
     */
    public void setJob(String job);
    /**
     * 获取观察人员的职务
     */
    public String getJob();


}

Watcher

public class Watcher  implements WatcherObserver{
    private String job;

    @Override
    public void update(WaterQualitySubject subject) {
        //这里采用的是拉的方式
        System.out.println(job+"获取到通知,当前污染级别为:" + subject.getPolluteLevel());
    }

    @Override
    public void setJob(String job) {
        this.job = job;
    }

    @Override
    public String getJob() {
        return this.job;
    }
}

Test

public class Test {
    public static void main(String[] args) {
        //创建水质主题对象
        WaterQuality subject = new WaterQuality();
        //创建几个观察者
        WatcherObserver watcher1 = new Watcher();
        watcher1.setJob("监测人员");
        WatcherObserver watcher2 = new Watcher();
        watcher2.setJob("预警人员");
        WatcherObserver watcher3 = new Watcher();
        watcher3.setJob("监测部门领导");
        //注册观察者
        subject.attach(watcher1);
        subject.attach(watcher2);
        subject.attach(watcher3);
        //填写水质报告
        System.out.println("当水质为正常的时候-----------");
        subject.setPolluteLevel(0);
        System.out.println("当水质为轻度污染的时候-----------");
        subject.setPolluteLevel(1);
        System.out.println("当水质为中度污染的时候-----------");
        subject.setPolluteLevel(2);
    }
}

运行结果:
高手眼中的观察者模式和新手有什么不一样_第7张图片

可以看到根据预警级别通知了不同的观察者。
主要逻辑在目标对象实现类上,在if的判断上是比较取巧的写法,不是if 。。else if … .,而是多个if。组合if,好好体会学习,以后借鉴使用下。

二、如何不依赖抽象观察者,也能实现观察者模式:

上面的例子,都是有个抽象观察者的角色的,目标对象直接操作抽象观察者。如果不想使用抽象观察者,考虑的思路如下:

  • 1.新建抽象观察者类这个事情,别人给提供,我们实现它就好了,这里我们可以使用java自带的观察者模式,他已经帮我们自动提供了抽象观察者,和抽象目标类,我们按照他的规则直接使用就可以了。
  • 这里一会展示 使用java自带的观察者模式的例子:
  • 2.注册观察者和通知观察者的工作交给一个第三方,解耦目标和观察者。
    这种实现的方式很多,主要思路是反射委托

这里一会展示4个例子:

  • 反射委托实现上面第2个例子,上课看nba和动漫,老师来了,做出不同反应的例子。
  • 比如:模仿swing组件实现原理的例子(这个例子很特别,观察者可以观察多个目标对象
  • spring中listener实现观察者模式的例子
  • springboot使用Guava框架提供的eventBus,实现事件处理的例子。

1.使用java自带的观察者模式的例子

java提供抽象观察者Observer,抽象目标对象Observable,通知观察者的方法名必须是update。通知前必须调动setChange()方法,具体代码如下:

具体目标类(被观察者)

/**
 * Title: GPer
 * Description: JDK提供的一种观察者的实现方式,被观察者
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-03
 */
public class GPer extends Observable {
    private String name = "GPer生态圈";
    private static GPer gper = null;

    private GPer() {
    }

    public static GPer getInstance(){
        if(null == gper){
            gper = new GPer();
        }
        return gper;
    }

    public void publishQuestion(Question question){
        System.out.println(question.getUserName() + "在" + this.name + "上提交了一个问题。");
        setChanged();
        notifyObservers(question);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

来个辅助的类,和观察者模式没啥关系的业务类:Question

/**
 * Title: Question
 * Description: TODO
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-03
 */
public class Question {
    //提问者
    private String userName;
    //提问问题
    private String content;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }
}

具体的观察者类:Teacher

public class Teacher implements Observer {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Teacher(String name) {
        this.name = name;
    }

    @Override
    public void update(Observable o, Object arg) {
        GPer gper = (GPer)o;
        Question question = (Question)arg;
        System.out.println("===============================");
        System.out.println(name + "老师,你好!\n" +
                "您收到了一个来自“" + gper.getName() + "”的提问,希望您解答,问题内容如下:\n" +
                question.getContent() + "\n" +
                "提问者:" + question.getUserName());
    }
}

测试类:

public class ObserverTest {
    public static void main(String[] args) {
        GPer gper = GPer.getInstance();
        Teacher tom = new Teacher("Tom");
        Teacher mic = new Teacher("Mic");

        //这为没有@Tom老师
        Question question = new Question();
        question.setUserName("小明");
        question.setContent("观察者设计模式适用于哪些场景?");
        gper.addObserver(tom);
        gper.addObserver(mic);
        gper.publishQuestion(question);
    }
}

运行结果:
高手眼中的观察者模式和新手有什么不一样_第8张图片
查看结果,完美的展示了目标对象通知所有观察者的实现。

2.反射委托实现上面第2个例子,学生根据老师到来,做出不同反应的例子。

具体代码:
高手眼中的观察者模式和新手有什么不一样_第9张图片


/**
 * 事件对象的封装类
 *
 * @author Administrator
 */
public class Event {
    //要执行方法的对象   
    private Object object;
    //要执行的方法名称   
    private String methodName;
    //要执行方法的参数   
    private Object[] params;
    //要执行方法的参数类型   
    private Class[] paramTypes;

    public Event() {

    }

    public Event(Object object, String methodName, Object... args) {
        this.object = object;
        this.methodName = methodName;
        this.params = args;
        contractParamTypes(this.params);
    }

    //根据参数数组生成参数类型数组   
    private void contractParamTypes(Object[] params) {
        this.paramTypes = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            this.paramTypes[i] = params[i].getClass();
        }
    }


    public Object getObject() {
        return object;
    }

    //这里省略了若干get和set方法

    /**
     * 根据该对象的方法名,方法参数,利用反射机制,执行该方法
     *
     * @throws Exception
     */
    public void invoke() throws Exception {
        Method method = object.getClass().getMethod(this.getMethodName(), this.getParamTypes());
        if (null == method) {
            return;
        }
        method.invoke(this.getObject(), this.getParams());
    }


    public void setObject(Object object) {
        this.object = object;
    }

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Object[] getParams() {
        return params;
    }

    public void setParams(Object[] params) {
        this.params = params;
    }

    public Class[] getParamTypes() {
        return paramTypes;
    }

    public void setParamTypes(Class[] paramTypes) {
        this.paramTypes = paramTypes;
    }
}

EventHandler


/**
 * Title: EventHandler
 * Description: 事件的 处理者
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-05
 */
public class EventHandler {
    //是用一个List
    private List<Event> objects;

    public EventHandler(){
        objects=new ArrayList<Event>();
    }
    //添加某个对象要执行的事件,及需要的参数
    public void addEvent(Object object,String methodName,Object...args){
        objects.add(new Event(object,methodName,args));
    }
    //通知所有的对象执行指定的事件
    public void notifyX() throws Exception{
        for(Event e : objects){
            e.invoke();
        }
    }
}

通知者的 抽象类Notifier

public abstract class Notifier {
    private EventHandler eventHandler = new EventHandler();

    public EventHandler getEventHandler() {
        return eventHandler;
    }

    public void setEventHandler(EventHandler eventHandler) {
        this.eventHandler = eventHandler;
    }

    /**
     * 增加需要帮忙 放哨 的 学生
     *
     * @param object     要执行方法的对象
     * @param methodName 执行方法 的方法名
     * @param args       执行方法的参数
     */
    public abstract void addListener(Object object, String methodName,  Object... args);

    /**
     * 告诉所有要帮忙放哨的学生:老师来了
     */
    public abstract void notifyX();
}

通知者 GoodNotifier

public class GoodNotifier extends Notifier {

    @Override
    public void addListener(Object object, String methodName, Object... args) {
        System.out.println("有新的同学委托尽职尽责的放哨人!");
        EventHandler handler = this.getEventHandler();
        handler.addEvent(object, methodName, args);
    }

    @Override
    public void notifyX() {
        System.out.println("尽职尽责的放哨人告诉所有需要帮忙的同学:老师来了");
        try{
            this.getEventHandler().notifyX();
        }catch(Exception e){
            e.printStackTrace();
        }
    }


WatchCartoonListener

/**
 * Title: WatchCartoonListener
 * Description:  具体监听者(观察者)
 *
 * @author hfl
 * @version V1.0
 * @date 2020-06-05
 */
public class WatchCartoonListener extends GoodNotifier {

    public WatchCartoonListener() {
        System.out.println("WatchCartoonListener 我正在看漫画,开始时间:"+ LocalDateTime.now().toString());
    }

    public void stopPlayingGame(Date date){
        System.out.println("WatchCartoonListener  停止看漫画了,结束时间:"+ new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
    }
}

WatchingNBAListener

public class WatchingNBAListener extends GoodNotifier {
    public WatchingNBAListener() {
        System.out.println("WatchingNBAListener我正在看NBA,开始时间是: " + LocalDateTime.now().toString());
    }
    public void stopWatchingTV(Date date){
        System.out.println("WatchingNBAListener 快关闭NBA直播 , 结束时间是:" +  new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(date));
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //创建一个尽职尽责的放哨者
        Notifier goodNotifier = new GoodNotifier();

//创建一个玩游戏的同学,开始玩游戏
        WatchCartoonListener playingGameListener = new WatchCartoonListener();

//创建一个看电视的同学,开始看电视
        WatchingNBAListener watchingTVListener = new WatchingNBAListener();
//玩游戏的同学告诉放哨的同学,老师来了告诉一下
        goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看电视的同学告诉放哨的同学,老师来了告诉一下
        goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
        try {
            //一点时间后
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
//老师出现,放哨的人通知所有要帮忙的同学:老师来了
        goodNotifier.notifyX();


    }
}

运行结果:

public class Test {
    public static void main(String[] args) {
        //创建一个尽职尽责的放哨者
        Notifier goodNotifier = new GoodNotifier();

//创建一个玩游戏的同学,开始玩游戏
        WatchCartoonListener playingGameListener = new WatchCartoonListener();

//创建一个看电视的同学,开始看电视
        WatchingNBAListener watchingTVListener = new WatchingNBAListener();
//玩游戏的同学告诉放哨的同学,老师来了告诉一下
        goodNotifier.addListener(playingGameListener, "stopPlayingGame", new Date());
//看电视的同学告诉放哨的同学,老师来了告诉一下
        goodNotifier.addListener(watchingTVListener, "stopWatchingTV", new Date());
        try {
            //一点时间后
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
//老师出现,放哨的人通知所有要帮忙的同学:老师来了
        goodNotifier.notifyX();


    }
}

运行结果:
高手眼中的观察者模式和新手有什么不一样_第10张图片

事件委托机制 分析
1.放哨者完全不知道做游戏者的存在,完全解耦。(当然,功劳归功于Event和EventHandler,且这两个类具有通用性)
2.老师来了后游戏者停止游戏回到座位,看NBA者停止看NBA,看漫画这停止看漫画,玩游戏这停止玩游戏。(一次通知,执行了不同类的不同方法)
3.扩展性很高,再来一个打篮球的学生就先写个打篮球学生类,并在测试代码中告诉放哨者一下就好,放哨者完全没有变。重用性好

看了这个例子,再来了解swing组件监听的例子就简单多了,因为原理完全一样,都是有事件和事件处理者管理和调用观察者的方法。

3.swing组件监听的例子

具体看下代码:

高手眼中的观察者模式和新手有什么不一样_第11张图片

这个例子比较强大,既体现了委托注册观察者,又有某个观察者的方法对应某个目标类的具体方法,方法到方法的对应。好好理解,学习,争取项目中使用下:
Event

public class Event {
    //事件源,事件是由谁发起的保存起来
    private Object source;
    //事件触发,要通知谁
    private Object target;
    //事件触发,要做什么动作,回调
    private Method callback;
    //事件的名称,触发的是什么事件
    private String trigger;
    //事件触发的时间
    private long time;

    public Event(Object target, Method callback) {
        this.target = target;
        this.callback = callback;
    }

    public Event setSource(Object source) {
        this.source = source;
        return this;
    }

    public Event setTime(long time) {
        this.time = time;
        return this;
    }
    public Event setTrigger(String trigger) {
        this.trigger = trigger;
        return this;
    }
    public Object getSource() {
        return source;
    }


    public long getTime() {
        return time;
    }

    public Object getTarget() {
        return target;
    }

    public Method getCallback() {
        return callback;
    }

    @Override
    public String toString() {
        return "Event{" + "\n" +
                "\tsource=" + source.getClass() + ",\n" +
                "\ttarget=" + target.getClass() + ",\n" +
                "\tcallback=" + callback + ",\n" +
                "\ttrigger='" + trigger + "',\n" +
                "\ttime=" + time + "'\n" +
                '}';
    }
}

EventLisenter

public class EventLisenter {
    //JDK底层的Lisenter通常也是这样来设计的
    protected Map<String, Event> events = new HashMap<String, Event>();

    //事件名称和一个目标对象来触发事件
    public void addLisenter(String eventType, Object target) {
        try {
            this.addLisenter(
                    eventType,
                    target,
                    target.getClass().getMethod("on" + toUpperFirstCase(eventType), Event.class));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void addLisenter(String eventType, Object target, Method callback) {
        //注册事件
        events.put(eventType, new Event(target, callback));
    }


    //触发,只要有动作就触发
    private void trigger(Event event) {
        event.setSource(this);
        event.setTime(System.currentTimeMillis());

        try {
            //发起回调
            if (event.getCallback() != null) {
                //用反射调用它的回调函数
                event.getCallback().invoke(event.getTarget(), event);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //事件名称触发
    protected void trigger(String trigger) {
        if (!this.events.containsKey(trigger)) {
            return;
        }
        trigger(this.events.get(trigger).setTrigger(trigger));
    }

    //逻辑处理的私有方法,首字母大写
    private String toUpperFirstCase(String str) {
        char[] chars = str.toCharArray();
        chars[0] -= 32;
        return String.valueOf(chars);
    }

}

Mouse

public class Mouse extends EventLisenter {

    public void click(){
        System.out.println("调用单击方法");
        this.trigger(MouseEventType.ON_CLICK);
    }

    public void doubleClick(){
        System.out.println("调用双击方法");
        this.trigger(MouseEventType.ON_DOUBLE_CLICK);
    }

    public void up(){
        System.out.println("调用弹起方法");
        this.trigger(MouseEventType.ON_UP);
    }

    public void down(){
        System.out.println("调用按下方法");
        this.trigger(MouseEventType.ON_DOWN);
    }

    public void move(){
        System.out.println("调用移动方法");
        this.trigger(MouseEventType.ON_MOVE);
    }

    public void wheel(){
        System.out.println("调用滚动方法");
        this.trigger(MouseEventType.ON_WHEEL);
    }

    public void over(){
        System.out.println("调用悬停方法");
        this.trigger(MouseEventType.ON_OVER);
    }

    public void blur(){
        System.out.println("调用获焦方法");
        this.trigger(MouseEventType.ON_BLUR);
    }

    public void focus(){
        System.out.println("调用失焦方法");
        this.trigger(MouseEventType.ON_FOCUS);
    }
}

MouseEventCallback

/**
 * 自己写的逻辑,用于回调
 * Created by Tom.
 */
public class MouseEventCallback {

    public void onClick(Event e){
        System.out.println("===========触发鼠标单击事件==========" + "\n" + e);
    }

    public void onDoubleClick(Event e){
        System.out.println("===========触发鼠标双击事件==========" + "\n" + e);
    }

    public void onUp(Event e){
        System.out.println("===========触发鼠标弹起事件==========" + "\n" + e);
    }

    public void onDown(Event e){
        System.out.println("===========触发鼠标按下事件==========" + "\n" + e);
    }

    public void onMove(Event e){
        System.out.println("===========触发鼠标移动事件==========" + "\n" + e);
    }

    public void onWheel(Event e){
        System.out.println("===========触发鼠标滚动事件==========" + "\n" + e);
    }

    public void onOver(Event e){
        System.out.println("===========触发鼠标悬停事件==========" + "\n" + e);
    }

    public void onBlur(Event e){
        System.out.println("===========触发鼠标失焦事件==========" + "\n" + e);
    }

    public void onFocus(Event e){
        System.out.println("===========触发鼠标获焦事件==========" + "\n" + e);
    }

}

MouseEventType

public interface MouseEventType {
    //单击
    String ON_CLICK = "click";

    //双击
    String ON_DOUBLE_CLICK = "doubleClick";

    //弹起
    String ON_UP = "up";

    //按下
    String ON_DOWN = "down";

    //移动
    String ON_MOVE = "move";

    //滚动
    String ON_WHEEL = "wheel";

    //悬停
    String ON_OVER = "over";

    //失焦
    String ON_BLUR = "blur";

    //获焦
    String ON_FOCUS = "focus";
}

MouseEventTest

public class MouseEventTest {
    public static void main(String[] args) {

        MouseEventCallback callback = new MouseEventCallback();

        Mouse mouse = new Mouse();

        //@谁?  @回调方法
        mouse.addLisenter(MouseEventType.ON_CLICK,callback);
        mouse.addLisenter(MouseEventType.ON_FOCUS,callback);

        mouse.click();

        mouse.focus();


    }
}
public class Keybord extends EventLisenter {

    public void down(){

    }

    public void up(){

    }

}

运行结果:
高手眼中的观察者模式和新手有什么不一样_第12张图片

4.spring中listener实现观察者模式的例子

Spring的事件机制用到了观察者模式。在此种模式中,一个目标物件管理所有相依于它的观察者物件,并且在它本身的状态改变时主动发出通知。这通常透过呼叫各观察者所提供的方法来实现。此种模式通常被用来实现事件处理系统。
在Spring中各个listener相当于观察者。event事件相当于目标,观察者,而容器用来管理和注册观察者,发布事件。

具体的演示代码如下:
高手眼中的观察者模式和新手有什么不一样_第13张图片

事件类:OrderEvent 相当于目标类

public class OrderEvent extends ApplicationEvent {

    public OrderEvent(Object source) {
        super(source);
    }
}

OrderSmsListener相当于观察者

@Component
public class OrderSmsListener implements ApplicationListener<OrderEvent> {
    @Override
    public void onApplicationEvent(OrderEvent orderEvent) {
        System.out.println("orderSmsListener receive event from " + orderEvent.getSource());
    }
}

业务类:

@Service
public class OrderService {

    @Autowired
    private ApplicationContext applicationContext;


    public void order() {
        applicationContext.publishEvent(new OrderEvent("orderService"));
    }
}

另外spring容器相当于委托类。
测试:

@RunWith(SpringRunner.class)
@SpringBootTest
public class PatternApplicationTest {

    @Autowired
    private OrderService orderService;

    @Test
    public void contextLoads() {
    }


    @Test
    public void testOrder() {
        orderService.order();
    }

}

运行结果:
高手眼中的观察者模式和新手有什么不一样_第14张图片
显示,事件发布后,监听这就能观察到这个事件了,可以做更多的操作。

5.springboot中集成谷歌的Guava,通过eventBus实现订阅发布功能

EventBus无需实现复杂的事件、监听者、发布者。前面讲的是面向类,现在:Guava是面向是方法,更加强大。能够轻松落地观察模式的一种解决方案。

演示个例子:
引入Guava的包:

<dependency>
            <groupId>com.google.guavagroupId>
            <artifactId>guavaartifactId>
            <version>20.0version>
        dependency>

EventBus 事件总线

//api封装
public class MyEventBus {

    /** 事件任务总线 */
    private final static EventBus tiemEventBus = new EventBus();
    /**
     * 触发同步事件
     *
     * @param event
     */
    public static void post(Object event) {
        tiemEventBus.post(event);
    }
    /**
     * 注册事件处理器
     *
     * @param handler
     */
    public static void register(Object handler) {
        tiemEventBus.register(handler);
    }
    /**
     * 注销事件处理器
     *
     * @param handler
     */
    public static void unregister(Object handler) {
        tiemEventBus.unregister(handler);
    }
}

消息实体类

public class Message {
    private MessageType messageType;
    private String messageContent;

    public Message(MessageType messageType, String messageContent) {
        this.messageType = messageType;
        this.messageContent = messageContent;
    }
    public MessageType getMessageType() {
        return messageType;
    }
    public void setMessageType(MessageType messageType) {
        this.messageType = messageType;
    }
    public String getMessageContent() {
        return messageContent;
    }
    public void setMessageContent(String messageContent) {
        this.messageContent = messageContent;
    }

    public enum MessageType {
        OPENDOOR(1, "openDoor"),
        CLOSEDOOR(2,"closeDoor");
        private int code;
        private String value;

        MessageType(int code, String value) {
            this.code = code;
            this.value = value;
        }
        public int getCode() {
            return code;
        }
        public void setCode(int code) {
            this.code = code;
        }
        public String getValue() {
            return value;
        }
        public void setValue(String value) {
            this.value = value;
        }
        }
}

事件监听者

@Component
abstract class MyApplicationListener implements ApplicationListener<ApplicationPreparedEvent> {
    /**
     *  ApplicationPreparedEvent 上下文准备事件
     * @param applicationPreparedEvent
     */
    @Override
    public void onApplicationEvent(ApplicationPreparedEvent applicationPreparedEvent) {
        ConfigurableApplicationContext applicationContext = applicationPreparedEvent.getApplicationContext();
        MyApplicationListener bean = applicationContext.getBean(this.getClass());
        System.out.println("regist listener to eventBus...."+bean);
        MyEventBus.register(bean);
    }
}

订阅者(也即监听者)继承至MyApplicationListener。

@Component
public class MyListentenerSubscribe extends MyApplicationListener{
    @Subscribe
    public void on(Message message){
        System.out.println("subscribe message->  messgeType:"+message.getMessageType()+"\n messageContent:"+message.getMessageContent());
    }
}

测试:

@RestController
public class EventPublishCtrl extends LogBase {
    @GetMapping("/publish")
    public void publishEvent() {
        log.info("this publish method...");
        MyEventBus.post(new Message(Message.MessageType.OPENDOOR,"芝麻开门!"));
    }
}

启动项目,然后调用发布方法:
在这里插入图片描述

订阅者收到具体的消息类型,以及消息内容。


三、观察者模式的本质

在这里插入图片描述
高手眼中的观察者模式和新手有什么不一样_第15张图片

上面主要介绍了观察者模式的基本经典结构;入门小demo;使用的2种方式;观察者模式变形写法;java中封装好的观察者模式使用方式;不依赖抽象观察者的方式,比如使用反射委托例子,swing使用观察者的例子,spring中listener观察者模式实现的例子,springboot 的eventBus的观察者模式的例子. 最后说下观察者模式的本质。

自此,观察者模式总算写完了,其实后续可能会有加上mq的例子,还没深入学习,先这样吧。

参考文章:
https://blog.csdn.net/gdutxiaoxu/article/details/51824769
https://blog.csdn.net/fw19940314/article/details/100010397
https://www.cnblogs.com/wkzhao/p/10229283.html


个人微信公众号:
搜索: 怒放de每一天
不定时推送相关文章,期待和大家一起成长!!
在这里插入图片描述


你可能感兴趣的:(设计模式,java基础,spring家族)