监听器模式和观察者模式的关系,写点你不知道的

简介: 监听器模式和观察者模式的关系,写点你不知道的

前言

无论大家在实践中是否自己实现过观察者模式或监听器模式,但肯定间接使用过。比如Spring的事件机制,大多数人肯定都用过,只是没留意而已。


今天这篇文章主要围绕观察者模式、监听器模式,以及它们之间的关系展开。不仅用实例介绍它们的使用,而且也会聊一聊Spring事件机制对观察者模式的实践。


监听器模式和观察者模式怎么看起来是一样的?


先聊聊设计模式

为什么要使用监听模式,直接调用不好吗?这我们就要说说设计模式的好处了。设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。同时,采用设计模式之后,代码能够达到低耦合、低依赖的效果。


也就是说,即便不用设计模式,直接硬编码也能实现。但如果考虑到代码的耦合性、依赖性、扩展性等问题,设计模式便是更好的选择。比如观察者模式就能达到解耦、异步等效果。


观察者模式的定义

观察者模式定义了对象之间的一对多依赖,即一个主题对应多个观察者。当一个主题对象改变状态时,它的所有依赖者(观察者)都会收到通知并自动更新。比如用户订阅某个订阅号或公众号,当发新消息时,会发送给所有订阅者。


image.png观察者模式解决的是对象和对象之间的依赖关系。当多个对象依赖一个对象的关系时,一个主题对象状态改变,需要通知所有观察者对象。


监听器模式并不是一个新的设计模式,它是观察者模式在特定场景下的一种改造和应用。通常,观察者模式的主题在通知观察者时,通知中不包含任何信息。如果这个过程中携带了一些其他信息,那么主题本身就成为了事件源,而携带信息的封装类就成为了事件。此时的观察者模式,也就升级为监听器了。监听器模式是观察者模式的另一种形态。


观察者模式实例

先来看看观察者模式的代码实现,可以直接使用JDK自带的Observer,也可以自定义对应的API。单从JDK自带观察者模式的API,也可以看出该设计模式的分量(虽然在Java被废弃了)。


我们这里采用自定义相关类,主要包括主题和观察者两种对象。


首先定义一个主题对应的接口Subject:


public interface Subject {
    /**
     * 注册定义
     */
    void registerObserver(Observer observer);
    /**
     * 发送通知
     */
    void notifyObservers(Object msg);
}

主题接口中定义了两个方法一个用来注册观察者,一个用来发送通知。定义这个主题的具体实现类ConcreteSubject:

public class ConcreteSubject implements Subject {
    /**
     * 观察者集合
     */
    private List<Observer> observers = new ArrayList<>();
    @Override
    public void registerObserver(Observer observer) {
        // 添加订阅关系
        observers.add(observer);
    }
    @Override
    public void notifyObservers(Object msg) {
        // 通知订阅者
        for (Observer observer : observers) {
            observer.update(msg);
        }
    }
}

实现类中存储了,观察者的集合,这样就实现了主题和观察者之间一对多的关系。


创建一个观察者接口Observer,方便管理:


public interface Observer {

   // 处理业务逻辑

   void update(Object msg);

}

1

2

3

4

定义观察者接口的具体实现类ConcreteObserver:


public class ConcreteObserver implements Observer {
    @Override
    public void update(Object msg) {
        // 业务逻辑实现
        System.out.println("ConcreteObserver 接收到主题的消息: " + msg);
    }
}

在实现类中,打印一行消息。当然,在实践的过程中,这个实现类可以通过匿名类的形式创建,这样就具体的匿名类就在registerObserver时定义了。

下面来测试一下:

public class ObserverTest {
    public static void main(String[] args) {
        Subject subject = new ConcreteSubject();
        Observer observer = new ConcreteObserver();
        // 注册观察者
        subject.registerObserver(observer);
        // 发布消息
        subject.notifyObservers("来自Subject的消息");
    }
}

执行程序,打印结果:


ConcreteObserver 接收到主题的消息: 来自Subject的消息

1

说明可以正常接收到主题发布的消息。


在上面的实现中,可以看出已经达到了解耦合的效果,同时减少了依赖关系。每个观察者根本不需要知道发布者处理了什么业务逻辑,也不依赖于发布者的业务模型,只关心自己的逻辑处理即可。


监听模式实例

监听器模式通常包含三个角色:事件源、事件对象、事件监听器。如果观察者模式中的类名和方法对照改一下,并不改变业务逻辑,我们来看看是啥效果。


比如,将Observer改名为Listener,将其update方法改为onClick();将Subject的实现类ConcreteSubject改名为ListenerSupport,将registerObserver方法更名为addListener……


定义一个事件对象Event,用来传递事件信息:


public class Event {
    private String data;
    private String type;
    Event(String data, String type) {
        this.data = data;
        this.type = type;
    }
    // 省略getter/setter
}

原来主题的订阅对象Observer改名为Listener之后,成为监听器:

public interface Listener {
    void onClick(Event event);
}
• 1
• 2
• 3

为监听器提供一个实现类,当然在实践中也可以采用匿名类创建的方式:

public class ListenerA implements Listener {
    @Override
    public void onClick(Event event) {
        System.out.println("触发事件,type:" + event.getType() + ",data:" + event.getData());
    }
}

原来的主题ConcreteSubject对照ListenerSupport成为事件管理器:

public class ListenerSupport {
    private List<Listener> listeners = new ArrayList<>();
    public void addListener(Listener listener) {
        listeners.add(listener);
    }
    public void triggerEvent(Event event) {
        for (Listener listener : listeners) {
            listener.onClick(event);
        }
    }
}

对应的测试代码:

public class EventTest {
    public static void main(String[] args) {
        Listener listener = new ListenerA();
        ListenerSupport listenerSupport = new ListenerSupport();
        listenerSupport.addListener(listener);
        listenerSupport.triggerEvent(new Event("dataA", "typeA"));
    }
}

执行程序,打印信息如下:


触发事件,type:typeA,data:dataA

1

通过上面的对照代码,我们可以看出,即便业务逻辑不变,经过重命名的观察者模式已经变为监听器模式了。而它们的对照关系是:事件源对照ConcreteSubject(主题)、事件对象对照update方法的Object、事件监听器对照ConcreteObserver(订阅者)。这也再次证明了所说的“监听器模式是观察者模式的另一种形态”。


观察者模式和监听器模式对比

用一张图,来比较观察者模式和监听器模式的联系和区别:


image.png通过对比可以发监听器模式的优势是:在很多场景中,通知中附带了一些必不可少的其他信息,而事件Event可以对这些信息进行封装,使它本身拥有了多态的特性。每个事件对象就可以包含不同的信息。从这个层面来说,事件监听器模式是对观察者模式进行了进一步的抽象。


Spring中的最佳实践

观察者模式的经典应用算是Spring事件驱动模型了,它便是基于观察者模式实现的,同时也是项目中最常见的事件监听器。


Spring中观察者模式包含四个角色:事件、事件监听器、事件源、事件管理。


事件:ApplicationEvent是所有事件对象的父类。ApplicationEvent继承自jdk的EventObject,所有的事件都需要继承ApplicationEvent,并且通过source得到事件源。Spring 提供了很多内置事件,比如:ContextRefreshedEvent、ContextStartedEvent、ContextStoppedEvent、ContextClosedEvent、RequestHandledEvent。


事件监听器:ApplicationListener,也就是观察者,继承自jdk的EventListener,该类中只有一个方法onApplicationEvent,当监听的事件发生后该方法会被执行。


事件源:ApplicationContext,ApplicationContext是Spring的核心容器,在事件监听中ApplicationContext可以作为事件的发布者,也就是事件源。因为ApplicationContext继承自ApplicationEventPublisher。在ApplicationEventPublisher中定义了事件发布的方法:publishEvent(Object event)。


事件管理:ApplicationEventMulticaster,用于事件监听器的注册和事件的广播。监听器的注册就是通过它来实现的,它的作用是把Applicationcontext发布的Event广播给它的监听器列表。


小结

通过本篇文章我们知道,监听器模式的本质就是观察者模式,先将回调函数注册到被观察对象,当被观察对象发生变化时,通过回调函数告知观察者/监听者。而Spring中事件管理也是基于观察者模式实现的,算是一个比较经典的案例。



目录
相关文章
|
9月前
|
设计模式 Java
Java设计模式【二十二】:空对象模式
Java设计模式【二十二】:空对象模式
69 0
|
9月前
|
设计模式 前端开发 JavaScript
观察者模式 vs 发布-订阅模式:两种设计模式的对决!
欢迎来到前端入门之旅!这个专栏是为那些对Web开发感兴趣、刚刚开始学习前端的读者们打造的。无论你是初学者还是有一些基础的开发者,我们都会在这里为你提供一个系统而又亲切的学习平台。我们以问答形式更新,为大家呈现精选的前端知识点和最佳实践。通过深入浅出的解释概念,并提供实际案例和练习,让你逐步建立起一个扎实的基础。无论是HTML、CSS、JavaScript还是最新的前端框架和工具,我们都将为你提供丰富的内容和实用技巧,帮助你更好地理解并运用前端开发中的各种技术。
|
18天前
|
设计模式 Java Go
【再谈设计模式】状态模式~对象行为的状态驱动者
状态模式属于行为型设计模式。它将对象的行为封装在不同的状态类中,使得对象在不同的状态下表现出不同的行为。上下文(Context):这是一个包含状态对象的类,它定义了客户感兴趣的接口,并维护一个具体状态对象的引用。上下文将操作委托给当前的状态对象来处理。抽象状态(State):这是一个抽象类或者接口,它定义了一个特定状态下的行为接口。所有具体的状态类都实现这个接口。具体状态(Concrete State):这些是实现抽象状态接口的类,每个具体状态类实现了与该状态相关的行为。
58 18
|
9月前
|
存储 设计模式 JavaScript
第六篇 再谈观察者模式的具体应用,如监听一个class其中一个属性,如websocket中onmessage的实现
第六篇 再谈观察者模式的具体应用,如监听一个class其中一个属性,如websocket中onmessage的实现
|
8月前
|
设计模式
观察者模式-大话设计模式
观察者模式-大话设计模式
|
8月前
|
数据管理 Java
Spigot开发中的事件与监听器的关系
在Spigot插件开发中,监听器(Listener)是一个非常重要的概念。它们允许你捕捉和处理各种游戏事件,使你的插件能够对玩家的行为、游戏环境的变化等做出响应。本文将详细介绍监听器是什么、它们的用途,并通过一个代码示例展示如何使用监听器。
67 0
|
9月前
|
设计模式
二十三种设计模式全面解析-当你的对象需要知道其他对象的状态变化时,观察者模式是你的救星!
二十三种设计模式全面解析-当你的对象需要知道其他对象的状态变化时,观察者模式是你的救星!
|
9月前
|
设计模式 监控 容器
设计模式-观察者模式(观察者模式的需求衍变过程详解,关于监听的理解)
设计模式-观察者模式(观察者模式的需求衍变过程详解,关于监听的理解)
|
JSON 数据格式
RxSwift核心之序列的创建、订阅与销毁
RxSwift核心之序列的创建、订阅与销毁
143 0
开门小例子学习观察者模式&事件与委托
3.2.一个喊话人(喊话人有权限),多个拿钥匙开门的人(每个人负责不同的门):米老师大喊给我开一下水麒麟的门——>此时听到消息并且拿着水麒麟钥匙的人就会过来开门

热门文章

最新文章