SpringBoot深度探究(四)观察者设计模式的演进

本文通过一个生动的花开花落场景,逐步解析了观察者设计模式的演变过程,从基础的线程监听到事件监听器的引入,详细阐述了观察者模式的核心特点:被观察者持有监听者的引用,支持增加和删除监听者,并在状态变化时通知监听者执行相应动作。最后,通过事件抽象,实现了对不同行为的多种响应,展示了观察者模式在实际问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

SpringBoot和SpringMVC有一个很大的改进,就是SpringBoot基于SpringMVC做了很多的事件监听模型。要弄清楚什么是Spring中的事件监听模型,首先要说明白什么是观察者设计模式。所以本篇将会从一个例子详细讲解观察者设计模式的演进。更多Spring内容进入【Spring解读系列目录】

场景模型

为了说清楚什么是观察者设计模式,先讲一个小的场景。场景很简单:假设一个花盆里有一朵花(Flower),花朵的旁边有两个测试人员小红(XiaoHong)和李华(LiHua)。当这朵花开放(bloom)的时候小红会开心的笑(laugh),但是李华会因为花粉过敏而流泪(tear)。

基础的观察者模型

既然说到演进,就先拿LiHua做这个例子。首先要实现花开了LiHua要流泪。于是就要有一个原也就是Flower类作为被观察者,然后LiHua也要有一个类,里面有一个流泪的动作。Flower作为被观察者,一定会有动作触发LiHua流泪的动作。传统来说应该在Flower里面有一个标志,一旦有触发开花的动作改变标志位,LiHua就应该流泪。于是LiHua这里应该有一个线程不断的监视开花的动作,不然开花了LiHua不流泪就不好了。而Flower这边也应该有另一个线程去执行修改标志位的动作。因此两个类都应该做个线程去执行这些事情,所以基于上面的设计,写出了下面的Sample。

LiHua类,监听Flower的开花状态:

public class LiHua implements Runnable{

    private Flower flower;

    public LiHua(Flower flower) {
        this.flower = flower;
    }

    public void tear(){
        System.out.println("花开,李华流泪");
    }

    @Override
    public void run() {
        System.out.println("李华开始观察是否花开");
        //无线循环监听是否开花了
        while(!flower.isBloom()){
            try { //每隔1s监听一次
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //如果开花了,跳出循环,执行流泪
        tear();
    }
}

Flower类,操作自己的开花状态:

public class Flower implements Runnable{
    //开花的变量
    private boolean bloom=false;

    public boolean isBloom() {
        return bloom;
    }

    public void bloomNow(){
        //修改开花变量
        bloom=true;
    }

    @Override
    public void run() {
        try {//模拟开花的时间
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bloomNow();
    }
}

测试类:

public class TestObserver {
    public static void main(String[] args) {
        Flower flower=new Flower();
        Thread threadf=new Thread(flower);
        threadf.start();

        LiHua LiHua=new LiHua(flower);
        Thread threadl=new Thread(LiHua);
        threadl.start();
    }
}

程序开始运行,当flower调用bloomNow()方法改变开花状态的时候,LiHua就会因为监听到开花了而执行tear()方法。虽然这种方法可以实现功能目标,但是其缺点也是非常明显的。我们看while(!flower.isBloom())非常类似于Socket,在这里一直等待数据写入。也就意味着LiHua线程非常的消耗资源,无论花开不开,一直需要盯着flower。如果有别的动作,比如吃饭那就无法切换。基于此我们可以针对这个问题做一个修改,那就是当flower开始的时候,通知LiHua来看就可以了。也就是说这种极度消耗资源的设计可以修改为:当某个事件发生的时候,主动通知它的监听者,就可以优化程序耗能。

观察者模型的第一次改进:角色互换

根据上面的分析,我们需要做的就是让Flower通知LiHua开花这个事件。上面的例子实际上是让LiHua持有Flower类,并一直监听Flower中的变量。如果要让Flower主动通知LiHua,那就意味着Flower类要持有LiHua类才可以。也就是说这样的改进将会使被观察者(Flower)拥有观察者(LiHua),这点要特别注意,因为这也是观察者设计模式中非常重要的一个概念。这点和我们正常理解的观察者与被观察者的情形是相反的:在观察者设计模式被观察者是主动的,而观察者才是被动的。

由于LiHua不需要一直看着Flower,首先LiHua可以不需要单独作为线程存在,Runable接口就可以被优化掉。其次Flower作为主动方,需要添加LiHua类作为传递的参数。由于LiHua类对象能够直接被Flower持有,所以也不需要在使用标识作为判断依据,只要开花立刻调用tear()流泪方法就可以了,因此第一次的改进如下。

LiHua类:

public class LiHua{
    //只保留动作
    public void tear(){
        System.out.println("花开,李华流泪");
    }
}

Flower类:

public class Flower implements Runnable{
    //持有LiHua类,并在开花动作以后,直接调用流泪方法
    private LiHua LiHua;

    public Flower(LiHua LiHua) {
        this.LiHua = LiHua;
    }

    public void bloomNow(){
       //由于直接通知,因此标识变量也可以优化掉
        LiHua.tear();
    }

    @Override
    public void run() {
        System.out.println("开花倒计时开始");
        try {//模拟开花的时间
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bloomNow();
    }
}

测试类:

public class TestObserver {
    public static void main(String[] args) {
        Flower flower=new Flower(new LiHua());
        Thread threadf=new Thread(flower);
        threadf.start();
    }
}

从上面的改进中,可以看到LiHua已经不需要在持续的占用资源去监听花开的状态,只要花开LiHua就触发流泪的动作。LiHua是否进行流泪的动作,取决于Flower而不是LiHua,这样修改性能就是一个很大的提升。

但是这样的修改,还是有问题的。因为如果按照这样设计,那么LiHua除了流泪什么都做不了,而且只有LiHua能够对花开进行反应。但是设想的场景里还有XiaoHong的角色和动作都无法加入进来。因此此时又引入了观察者模式的另一个概念:事件。

观察者模型的第二次改进:抽象事件

上面的改进导致了bloomNow()中开花动作的消失,只有LiHua动作被体现出来。那么可以预期,LiHua由于开花导致的动作可以被抽象提取出来,作为一个公用的行为action。不同的行为对应不同的动作或者不同人的不同动作。因此抽象出来的东西,就被称为事件。为了同时对开花这个事件进行不同的反应,因此就有了EventListener事件监听器的概念。到了这一步,整个程序并不在多少人观察了花的状态,也不在乎观察者看了花的状态以后执行的action是什么,只需要把应该通知的内容告诉观察者即可。那么此时观察者就变成被动的监听者。因此继续修改程序如下。

FlowerListener监听者接口:

public interface FlowerListener {
    public void action(FlowerEvent event);
}

LiHua类实现监听者接口:

public class LiHua implements FlowerListener{
    public void action(FlowerEvent event){
        if (1==event.getAction()){
            System.out.println("花开,李华流泪");
        }
    }
}

XiaoHong类实现监听者接口:

public class XiaoHong implements FlowerListener{
    public void action(FlowerEvent event){
        if (1==event.getAction()){
            System.out.println("花开,小红开始笑");
        }
    }
}

FlowerEvent开花的事件类:

public class FlowerEvent {
    private int action;

    public int getAction() {
        return action;
    }

    public void setAction(int action) {
        this.action = action;
    }
}

Flower类作为被观察者,持有观察者(LiHua,XiaoHong)的引用:

public class Flower implements Runnable{
    //为了实现一个事件多个监听,使用一个集合把所有监听的事件放进来
    private List<FlowerListener> flowerListeners=new ArrayList<>();

    //从外部添加事件
    public void addListeners(FlowerListener listener){
        flowerListeners.add(listener);
    }

    public void bloomNow(){
        FlowerEvent event=new FlowerEvent();
        event.setAction(1); //这里设置为1,当设置为2的时候可以有更多的反应
        //对于某一个事件,不同人的action表达不同的反应
        for (FlowerListener flowerListener : flowerListeners) {
            flowerListener.action(event);
        }
    }

    @Override
    public void run() {
        System.out.println("开花倒计时开始");
        try {//模拟开花的时间
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        bloomNow();
    }
}

测试类:

public class TestObserver {
    public static void main(String[] args) {
        Flower flower=new Flower();
        flower.addListeners(new LiHua()); //LiHua被动监听花的状态
        flower.addListeners(new XiaoHong()); //XiaoHong被动监听花的状态
        Thread threadf=new Thread(flower);
        threadf.start();
    }
}
当程序运行,同时对不同人进行不同的action:
开花倒计时开始
花开,李华流泪
花开,小红开始笑

可以看到经过这样的改进以后,被观察者(Flower)不仅可以在改变状态以后通知观察者(LiHua),而且可以支持增加和删除观察者。那么一个观察者模式的实现就正式结束了。

总结

那么我们总结一下观察者设计模式的特点:

  • 被观察者持有监听的观察者的引用。
  • 被观察者支持增加和删除观察者。
  • 被观察者主题状态改变,会通知观察者做相应的动作。

本篇讲观察者模式的概念和实现做了一个完整的介绍,下一篇【JDK观察者模式探究】将会讲解JDK如何实现的观察者模式的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值