前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >一文搞懂设计模式—观察者模式

一文搞懂设计模式—观察者模式

作者头像
BookSea
发布于 2024-02-29 05:46:53
发布于 2024-02-29 05:46:53
82401
代码可运行
举报
文章被收录于专栏:Java随想录Java随想录
运行总次数:1
代码可运行

本文字数:4159字,阅读大约需要 15 分钟。

观察者模式(Observer Pattern)是一种常见的行为型设计模式,用于在对象之间建立一种一对多的依赖关系。当一个对象的状态发生变化时,所有依赖它的对象都将得到通知并自动更新。

使用场景

观察者模式在许多应用中都有广泛的应用,特别是当存在对象之间的一对多关系,并且需要实时通知和更新时,观察者模式非常适用。下面列举几个典型的使用场景:

  • 消息发布/订阅系统:观察者模式可以用于构建消息发布/订阅系统,其中消息发布者充当主题(被观察者),而订阅者则充当观察者。当发布者发布新消息时,所有订阅者都会收到通知并执行相应操作。
  • 用户界面组件:在图形用户界面 (GUI) 开发中,观察者模式常被用于处理用户界面组件之间的交互。当一个组件的状态发生变化时,其他依赖该组件的组件将自动更新以反映新的状态。
  • 股票市场监控:在金融领域,观察者模式可用于实现股票市场监控系统。各个投资者可以作为观察者订阅感兴趣的股票,在股票价格变动时即时收到通知。
  • 事件驱动系统:观察者模式也常用于事件驱动系统中,如图形用户界面框架、游戏引擎等。当特定事件发生时,触发相应的回调函数并通知所有注册的观察者。

以上仅是观察者模式的一些典型使用场景,实际上,只要存在对象之间的依赖关系,并且需要实现解耦和灵活性,观察者模式都可以考虑作为一种设计方案。

实现方式

观察者模式包含以下几个核心角色:

  • 主题(Subject):也称为被观察者或可观察者,它是具有状态的对象,并维护着一个观察者列表。主题提供了添加、删除和通知观察者的方法。
  • 观察者(Observer):观察者是接收主题通知的对象。观察者需要实现一个更新方法,当收到主题的通知时,调用该方法进行更新操作。
  • 具体主题(Concrete Subject):具体主题是主题的具体实现类。它维护着观察者列表,并在状态发生改变时通知观察者。
  • 具体观察者(Concrete Observer):具体观察者是观察者的具体实现类。它实现了更新方法,定义了在收到主题通知时需要执行的具体操作。

下面是观察者模式的经典实现方式:

  1. 定义观察者接口:创建一个名为 Observer 的接口,包含一个用于接收通知的方法,例如 update()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Observer {
    void update();
}
  1. 定义主题接口:创建一个名为 Subject 的接口,包含用于管理观察者的方法,如 registerObserver()removeObserver()notifyObservers()
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
  1. 实现具体主题:创建一个具体类实现 Subject 接口,实现注册、移除和通知观察者的方法。在状态变化时调用 notifyObservers() 方法通知所有观察者。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import java.util.ArrayList;
import java.util.List;

public class ConcreteSubject implements Subject {
    private final List<Observer> observers = new ArrayList<>();
    private int state;

    public void setState(int state) {
        this.state = state;
        notifyObservers();
    }

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

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

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }
}
  1. 实现具体观察者:创建一个具体类实现 Observer 接口,实现接收通知并进行相应处理的方法。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ConcreteObserver implements Observer {
    private String name;
    private Subject subject;

    public ConcreteObserver(String name, Subject subject) {
        this.name = name;
        this.subject = subject;
    }

    @Override
    public void update() {
        System.out.println(name+" received notification");
    }
}
  1. 使用观察者模式:在实际代码中,我们可以创建具体的主题和观察者对象,并进行注册和触发状态变化。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Main {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();

        ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
        ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);

        subject.registerObserver(observer1);
        subject.registerObserver(observer2);

        subject.setState(10);
        // Output:
        // Observer 1 received notification
        // Observer 2 received notification

        subject.removeObserver(observer1);

        subject.setState(20);
        // Output:
        // Observer 2 received notification
    }
}

Java对观察者模式的支持

观察者模式在Java语言中的地位非常重要。在JDK的 java.util 包中,提供 Observable 类以及 Observer 接口,它们构成了Java语言对观察者模式的支持。

使用 Observable 类以及 Observer 接口,优化之后的代码为:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 具体观察者
public class ConcreteObserver implements Observer {
    private String name;

    public ConcreteObserver(String name) {
        // 设置每一个观察者的名字
        this.name = name;
    }

    /**
     * 当变化之后,就会自动触发该方法
     */
    @Override
    public void update(Observable o, Object arg) {
        if (arg instanceof Integer) {
            System.out.println(this.name + " 观察到 state 更改为:" + arg);
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 被观察者,继承 Observable 表示可以被观察
public class ConcreteSubject extends Observable {
    private int state;

    public ConcreteSubject(int state) {
        this.setState(state);
    }

    public int getState() {
        return state;
    }

    public void setState(int state) {
        // 设置变化点
        super.setChanged();
        // 状态变化,通知观察者
        super.notifyObservers(state);
        this.state = state;
    }

    @Override
    public String toString() {
        return "state:" + this.state;
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class TestObserve {
    public static void main(String[] args) {
        // 创建被观察者
        ConcreteSubject subject = new ConcreteSubject(0);
        // 创建观察者
        ConcreteObserver ConcreteObserverA = new ConcreteObserver("观察者 A");
        ConcreteObserver ConcreteObserverB = new ConcreteObserver("观察者 B");
        ConcreteObserver ConcreteObserverC = new ConcreteObserver("观察者 C");
        // 添加可观察对象
        subject.addObserver(ConcreteObserverA);
        subject.addObserver(ConcreteObserverB);
        subject.addObserver(ConcreteObserverC);

        System.out.println(subject);
        // Output:
        // state:0
        subject.setState(1);
        // Output:
        // 观察者 C 观察到 state 更改为:1
        // 观察者 B 观察到 state 更改为:1
        // 观察者 A 观察到 state 更改为:1
        System.out.println(subject);
        // Output:
        // state:1

    }
}

Guava对观察者模式的支持

Guava 中使用 Event Bus 来实现对观察者模式的支持。

com.google.common.eventbus.EventBus 提供了以下主要方法:

  • register(Object listener):将一个对象注册为事件的监听器。
  • unregister(Object listener):从事件总线中注销一个监听器。
  • post(Object event):发布一个事件到事件总线,以便通知所有注册的监听器。
  • getSubscribers(Class<?> eventClass):返回订阅指定事件类型的所有监听器的集合。

这些方法提供了事件的注册、注销、发布和获取监听器等功能,使得开发者可以方便地使用 EventBus 进行事件驱动编程。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Getter
@AllArgsConstructor
public class MyEvent {
    private String message;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
public class EventSubscriber {
    @Subscribe
    public void handleEvent(MyEvent event) {
        String message = event.getMessage();
        // Handle the event logic
        log.info("Received event: " + message);
    }
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Test
public void test() {
        EventBus eventBus = new EventBus();
        EventSubscriber subscriber = new EventSubscriber();
        eventBus.register(subscriber);

        // Publish an event
        eventBus.post(new MyEvent("Hello, World!"));
        // Output:
        // Received event: Hello, World!   
    }

Spring对观察者模式的支持

Spring 中可以使用 Spring Event 来实现观察者模式。

在Spring Event中,有一些核心的概念和组件,包括ApplicationEvent、ApplicationListener、ApplicationContext和ApplicationEventMulticaster。

  • ApplicationEvent(应用事件):
    • ApplicationEvent是Spring Event框架中的基础类,它是所有事件类的父类。
    • 通过继承ApplicationEvent,并定义自己的事件类,可以创建特定类型的事件对象。
    • 事件对象通常包含与事件相关的信息,例如状态变化、操作完成等。
  • ApplicationListener(应用监听器):
    • ApplicationListener是Spring Event框架中的接口,用于监听并处理特定类型的事件。
    • 通过实现ApplicationListener接口,并指定感兴趣的事件类型,可以创建具体的监听器。
    • 监听器可以定义在任何Spring Bean中,当所监听的事件被发布时,监听器会自动接收到该事件,并执行相应的处理逻辑。
  • ApplicationContext(应用上下文):
    • ApplicationContext是Spring框架的核心容器,它负责管理Bean的生命周期和依赖关系。
    • 在Spring Event中,ApplicationContext是事件的发布者和订阅者的容器。
    • 通过获取ApplicationContext实例,可以获取ApplicationEventPublisher来发布事件,也可以注册ApplicationListener来监听事件。
  • ApplicationEventMulticaster(事件广播器):
    • ApplicationEventMulticaster是Spring Event框架中的组件,用于将事件广播给各个监听器。
    • 它负责管理事件和监听器之间的关系,并将事件传递给对应的监听器进行处理。
    • Spring框架提供了几种实现ApplicationEventMulticaster的类,如SimpleApplicationEventMulticaster和AsyncApplicationEventMulticaster,用于支持不同的事件分发策略。

通过使用这些关键概念和组件,可以在 Spring 应用程序中实现事件驱动的编程模型。事件发布者(ApplicationEventPublisher)可以发布特定类型的事件,而订阅者(ApplicationListener)可以监听和处理已发布的事件。ApplicationContext作为容器,负责管理事件和监听器,并使用ApplicationEventMulticaster来实现事件的广播和分发。

下面是使用 Spring Event 实现观察者模式的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * <p>
 * 基础事件发布类
 * </p>
 *
 */

public abstract class BaseEvent<T> extends ApplicationEvent {

    /**
     * 该类型事件携带的信息
     */
    private T eventData;

    /**
     *
     * @param source 最初触发该事件的对象
     * @param eventData 该类型事件携带的信息
     */
    public BaseEvent(Object source, T eventData) {
        super(source);
        this.eventData = eventData;
    }

    public T getEventData() {
        return eventData;
    }
}

这里定义了一个基础事件发布抽象类,所有的事件发布类都可以继承此类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public  class User {
    private Integer userId;
    private String userName;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class UserEvent  extends BaseEvent<User>{
    private static final long serialVersionUID = 8145130999696021526L;

    public UserEvent(Object source, User user) {
        super(source,user);
    }

}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
@Service
public class UserListener {
    /*
     * @Async加了就是异步监听,没加就是同步(启动类要开启@EnableAsync注解)
     * 可以使用@Order定义监听者顺序,默认是按代码书写顺序
     * 如果返回类型不为void,则会被当成一个新的事件,再次发布
     * @EventListener注解在EventListenerMethodProcessor类被扫描
     * 可以使用SpEL表达式来设置监听器生效的条件
     * 监听器可以看做普通方法,如果监听器抛出异常,在publishEvent里处理即可
     */

    //@Async
    @Order(1)
    @EventListener(condition = "#userEvent.getEventData().getUserName().equals('小明')")
    public String lister1(UserEvent userEvent){
        User user =userEvent.getEventData();
        log.info(user.toString());
        return "小米";
    }

    @Async
    @Order(2)
    @EventListener
    public void lister3(UserEvent userEvent){
        log.info("监听者2");
    }
    @Async
    @Order(3)
    @EventListener
    public void lister2(String name){
        log.info("我叫:"+name);
    }
    
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
@SpringBootTest
@RunWith(SpringRunner.class)
public class ObserveTest {
    @Resource
    private ApplicationEventPublisher applicationEventPublisher;
    @Test
    public void test() {
        applicationEventPublisher.publishEvent(new UserEvent(this, new User(1, "小明")));
        // Output:
        // User(userId=1, userName=小明)
        // 我叫:小米
        // 监听者2
    }

}

IDEA 中可以直接跳转到对应的监听器。

相比于 Guava Event Bus,Spring Event 在实现观察者模式时具有以下优点:

  • 集成性:Spring Event 是 Spring 框架的一部分,可以与其他 Spring 组件(如 Spring Boot、Spring MVC 等)无缝集成。这使得在一个应用程序中使用 Spring Event 变得更加方便和统一。
  • 注解驱动:Spring Event 支持使用注解来声明事件监听器和发布事件。通过使用 @EventListener 注解,开发人员可以轻松定义事件监听器方法,并且不需要显式注册和注销监听器。

优缺点

观察者模式有以下几个优点:

  • 解耦性:观察者模式能够将主题和观察者之间的耦合度降到最低。主题与观察者之间都是松散耦合的关系,它们之间可以独立地进行扩展和修改,而不会相互影响。
  • 灵活性:通过使用观察者模式,可以动态地添加、删除和通知观察者,使系统更加灵活。无需修改主题或观察者的代码,就可以实现新的观察者加入和旧观察者离开的功能。
  • 一对多关系:观察者模式支持一对多的依赖关系,一个主题可以有多个观察者。这样可以方便地实现消息的传递和广播,当主题状态更新时,所有观察者都能得到通知。

虽然观察者模式具有许多优点,但也存在一些缺点:

  • 可能引起性能问题:如果观察者较多或通知过于频繁,可能会导致性能问题。每个观察者都需要接收通知并执行相应操作,当观察者较多时,可能会增加处理时间和系统负载。
  • 可能引起循环依赖:由于观察者之间可以相互注册,如果设计不当,可能会导致循环依赖的问题。这样会导致触发通知的死循环,造成系统崩溃或异常。
  • 顺序不确定性:在观察者模式中,观察者的执行顺序是不确定的。如果观察者之间有依赖关系,可能会产生意外的结果。

综上所述,观察者模式在许多场景下都非常有用,但在使用时需要注意性能问题、循环依赖和执行顺序等方面的考虑。

希望这篇文章能给你带来收获和思考,如果你也有可借鉴的经验和深入的思考,欢迎评论区留言讨论。如果本文对你有帮助,请帮忙点个在看或者点个赞👍🏻。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2024-02-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java随想录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Java设计模式-观察者模式
观察者模式: 又称‘发布-订阅’模式, 定义一种对象间的一对多依赖关系(多个观察者Observer监听某一主题Subject). 当主题状态发生改变时,所有依赖它的对象都得到通知并被自动更新. 核心
Java帮帮
2018/03/16
8280
Java设计模式-观察者模式
设计模式:观察者模式
观察者模式属于行为型设计模式,用于建立对象间的一对多依赖关系。当主题(Subject)状态变化时,所有依赖的观察者(Observer)会自动收到通知并更新。
用户11531739
2025/03/11
650
五分钟学会观察者模式
观察者模式:多个观察者同时监听一个主题对象,当主题对象发生改变时,它的所有观察者都会收到通知。
Java识堂
2020/03/25
3810
五分钟学会观察者模式
java设计模式-观察者模式
模式定义 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 设计原则 为交
三哥
2018/06/15
6770
观察者模式的思考
观察者模式(Observer Pattern)是一种行为型设计模式,它的起源可以追溯到20世纪90年代初,由设计模式四人帮(Erich Gamma, Richard Helm, Ralph Johnson 和 John Vlissides)在其著作《设计模式:可复用面向对象软件的基础》中首次提出。观察者模式用于解决对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。
不惑
2024/07/23
3610
观察者模式的思考
跟着GPT学设计模式之观察者模式
观察者模式(Observer Pattern)是一种行为型设计模式,它定义了对象之间的一对多依赖关系,使得当一个对象的状态发生改变时,其依赖对象都能够收到通知并自动更新。
codetrend
2024/06/06
1020
跟着GPT学设计模式之观察者模式
TypeScript 设计模式之观察者模式
在软件系统中经常碰到这类需求:当一个对象的状态发生改变,某些与它相关的对象也要随之做出相应的变化。这是建立一种对象与对象之间的依赖关系,一个对象发生改变时将自动通知其他对象,其他对象将相应做出反应。
pingan8787
2020/08/17
1.2K0
观察者模式详解
从字面意思上去理解,所谓的观察者模式,首先有观察者(一个或者多个),被观察者(一个)。当被观察者状态发生变化的时候,就会去通知它的所有的观察者,然后由观察者根据被观察者的情况作出反应。观察者模式属于行为型模式。
开发者
2019/12/26
4150
观察者模式详解
设计模式之观察者模式及典型应用
微信公众号有服务号、订阅号和企业号之分。以我的公众号为例,我的公众号类型是订阅号,名称是 "小旋锋",专注于大数据,Java后端类技术分享。目前主要是分享学习笔记为主,尽量做到 "原创"、"高质量"、"成体系"。每当我发布一篇博文推送,订阅的用户都能够在我发布推送之后及时接收到推送,即可方便地在手机端进行阅读。
小旋锋
2019/01/21
1.1K0
设计模式-观察者模式(一)
观察者模式是一种常用的设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听一个主题对象,当主题对象的状态发生改变时,它的所有观察者对象都会收到通知并自动更新。
堕落飞鸟
2023/05/04
1470
设计模式之观察者模式
  随着工作时间的越来越长,发现对设计模式缺失的坏处越来越明显,但是当你知道某种设计模式的实现方式以后,你会发现,其实工作中早已经玩过这些东西,但是你之前并不知道它属于设计模式的一种,今天就先介绍一种设计模式:观察者模式,然后我们也手动实现观察者模式以加深印象。
阿豪聊干货
2018/08/09
2310
设计模式之观察者模式
Java 中的观察者模式
----本文来源于Rohit Joshi的《Java Design Patterns》一书的Chapter7:Observer Design Pattern
码农小胖哥
2020/05/08
7120
Java 中的观察者模式
Java设计模式(十五)----观察者模式
观察者模式 一、定义 二、结构 具体案例 推模型和拉模型 三、Java提供的对观察者模式的支持 Observer接口 Observable类 一、定义 观察者模式是对象的行为模式,又叫发布-订阅(Publish/Subscribe)模式、模型-视图(Model/View)模式、源-监听器(Source/Listener)模式或从属者(Dependents)模式。 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,
汤高
2018/01/11
8320
Java设计模式(十五)----观察者模式
【地铁上的设计模式】--行为型模式:观察者模式
观察者模式(Observer Pattern)是一种设计模式,它定义了对象之间一对多的依赖关系,当一个对象状态发生改变时,其所有依赖者都会收到通知并自动更新。观察者模式中,被观察者对象通常被称为主题(Subject),而观察者对象通常被称为观察者(Observer)。 在观察者模式中,主题维护一个观察者列表,当主题的状态发生变化时,它会依次通知所有的观察者,使它们能够及时更新自己的状态。这种松耦合的设计模式使得主题和观察者之间的关系不会影响彼此的稳定性,使得系统更加灵活和易于扩展。
喵叔
2023/05/12
2890
C++设计模式——Observer观察者模式
观察者模式是一种行为型设计模式,又被称为"发布-订阅"模式,它定义了对象之间的一对多的依赖关系,当一个对象的状态发生变化时,所有依赖于它的对象都会收到通知并自动更新。
Coder-ZZ
2024/07/01
2670
C++设计模式——Observer观察者模式
设计模式---观察者模式
微信公众号有服务号、订阅号和企业号之分。当我们在公众号上发布一篇博文推送时,订阅的用户都能够在我发布推送之后及时接收到推送,即可方便地在手机端进行阅读。
大忽悠爱学习
2021/11/15
2640
设计模式-观察者模式
模式定义 定义了对象之间的一对多依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。主题(Subject)是被观察的对象,而其所有依赖者(Observer)称为观察者。 设计原则 为
武培轩
2018/04/18
8350
设计模式-观察者模式
设计模式之观察者模式
观察者模式又称为发布-订阅(Publish/Subscribe)模式,是23种设计模式之一。DP中是这么定义观察者模式的:
端碗吹水
2020/09/23
4680
设计模式之观察者模式
Java观察者模式(Observer)
  观察者模式java GOF23种设计模式中的一种。在最近介绍的Redis和ActiveMQ中都涉及到了观察者模式,所以我们在本文详细介绍下此模式:
用户4919348
2019/04/02
8060
Java观察者模式(Observer)
Java设计模式之(十二)——观察者模式
观察者模式(Observer Design Pattern):在对象之间定义一个一对多的依赖,当一个对象状态改变的时候,所有依赖的对象都会得到通知并自动更新。
IT可乐
2021/12/04
5.2K0
Java设计模式之(十二)——观察者模式
相关推荐
Java设计模式-观察者模式
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验