Java观察者模式源码剖析及使用场景

一、原理及通俗理解

观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,使得一个对象的状态发生变化时,其所有依赖者都会收到通知并自动更新。

观察者模式涉及两个主要角色:Subject(主题)和Observer(观察者)。

  • Subject:维护一个观察者列表,提供注册和移除观察者的方法,并在自身状态发生变化时,通知所有已注册的观察者。
  • Observer:对感兴趣的事物(Subject)的状态变化做出相应的反应。

就像订阅了某个博客的通知一样,当博客作者发布新文章时,所有订阅者都会收到通知。在这里,博客作者就是主题,而订阅者则是观察者。

二、股票项目中使用

需求描述:假设我们有一个股票交易系统,需要实现对股票价格变化的实时通知功能。当股票价格发生变化时,所有订阅该股票的用户应该能够接收到最新的股票价格信息。

  1. 定义主题接口(Subject) 包含注册观察者、移除观察者和通知观察者的方法
import java.util.ArrayList;
import java.util.List;

public interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers(String stockSymbol, double newPrice);
}
  1. 定义观察者接口(Observer) 包含更新股票价格的方法。
public interface Observer {
    void update(String stockSymbol, double newPrice);
}
  1. 实现具体的主题(StockExchange) StockExchange类实现了Subject接口,扮演股票交易所的角色。它维护了一个观察者列表、股票列表和股票价格列表。registerObserverremoveObserver方法用于注册和移除观察者。股票价格变化notifyObservers方法通知所有已注册的观察者。
public class StockExchange implements Subject {
    private List<Observer> observers; // 观察者列表
    private List<String> stocks; // 股票列表
    private List<Double> prices; // 股票价格

    public StockExchange() {
        observers = new ArrayList<>();
        stocks = new ArrayList<>();
        prices = new ArrayList<>();
    }
	
	//注册观察者
    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }
	
	//移除观察者
    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }
	
	//股票价格变化,通知所有已注册的观察者。
    @Override
    public void notifyObservers(String stockSymbol, double newPrice) {
        for (Observer observer : observers) {
            observer.update(stockSymbol, newPrice);
        }
    }

    // 添加新的股票和初始价格
    public void addStock(String stockSymbol, double initialPrice) {
        stocks.add(stockSymbol);
        prices.add(initialPrice);
    }

    // 更新股票价格并通知观察者
    public void updateStockPrice(String stockSymbol, double newPrice) {
        int index = stocks.indexOf(stockSymbol);
        if (index != -1) {
            prices.set(index, newPrice);
            notifyObservers(stockSymbol, newPrice);
        } else {
            System.out.println("Stock not found: " + stockSymbol);
        }
    }
}
  1. 实现具体的观察者(InvestorClient) InvestorClient类实现了Observer接口,扮演投资者的角色。它会在收到股票价格更新时打印出通知信息。
public class InvestorClient implements Observer {
    private String investorName;

    public InvestorClient(String investorName) {
        this.investorName = investorName;
    }
	
	//股票价格更新时打印出通知信息。
    @Override
    public void update(String stockSymbol, double newPrice) {
        System.out.println("Investor " + investorName + " received update: " + stockSymbol + " new price is $" + newPrice);
    }
}
  1. 使用示例main方法中,我们创建了一个StockExchange实例,添加了一些股票。然后创建了两个InvestorClient实例并注册为观察者。
public class StockTradingApp {
    public static void main(String[] args) {
        // 创建股票交易所
        StockExchange exchange = new StockExchange();

        // 添加股票
        exchange.addStock("AAPL", 120.0);
        exchange.addStock("GOOG", 2500.0);
        exchange.addStock("MSFT", 280.0);

        // 创建投资者客户端
        InvestorClient investor1 = new InvestorClient("John");
        InvestorClient investor2 = new InvestorClient("Jane");

        // 注册观察者
        exchange.registerObserver(investor1);
        exchange.registerObserver(investor2);

        // 更新股票价格
        exchange.updateStockPrice("AAPL", 125.5);
        exchange.updateStockPrice("GOOG", 2520.0);

        // 移除观察者
        exchange.removeObserver(investor2);

        // 再次更新股票价格
        exchange.updateStockPrice("MSFT", 290.0);
    }
}
  1. 输出: 模拟了几次股票价格更新,观察者会收到相应的通知,最后,我们移除了一个观察者,再次更新股票价格时,只有剩余的观察者会收到通知。
Investor John received update: AAPL new price is $125.5
Investor Jane received update: AAPL new price is $125.5
Investor John received update: GOOG new price is $2520.0
Investor Jane received update: GOOG new price is $2520.0
Investor John received update: MSFT new price is $290.0

三、Spring框架中使用观察者模式

  1. 在 Spring 的事件机制中,主要涉及以下几个核心组件:
  • ApplicationEvent: 事件的基类,扮演观察者模式中的主题(Subject)角色。
  • ApplicationListener:事件监听器接口,扮演观察者(Observer)角色,用于处理事件。
  • ApplicationEventMulticaster:事件广播器,负责将事件分发给注册的监听器。
  1. 主题角色,ApplicationEvent 继承自 EventObject类,EventObject 就是观察者模式中的主题,它维护了一个 source 属性,表示事件源,即产生事件的对象。
public abstract class ApplicationEvent extends EventObject {
    // ...

    /** Helper constructor for subclasses. */
    public ApplicationEvent(Object source) {
        super(source);
    }
    // ...
}
  1. 观察者角色,ApplicationListener 接口扩展自 EventListener,它定义了一个 onApplicationEvent 方法,用于处理传入的事件。每个事件监听器都需要实现这个接口,并编写处理逻辑。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}
  1. ApplicationEventMulticaster 接口定义了 multicastEvent 方法,用于将事件广播给所有注册的监听器。
public interface ApplicationEventMulticaster {
    void multicastEvent(ApplicationEvent event);
    void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);
    // ...
}
  1. Spring 提供了一个默认实现 SimpleApplicationEventMulticaster,它维护了一个 ApplicationListener 列表,在接收到事件时,会遍历这个列表,依次通知每个监听器。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {
    // ...

    @Override
    public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
        ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
        Executor executor = getTaskExecutor();
        for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
            // ...
            if (executor != null) {
                executor.execute(() -> invokeListener(listener, event));
            } else {
                invokeListener(listener, event);
            }
        }
    }

    private void invokeListener(ApplicationListener listener, ApplicationEvent event) {
        try {
            listener.onApplicationEvent(event);
        }
        catch (ClassCastException ex) {
            // ...
        }
    }

    // ...
}

7.在Spring框架中,观察者模式被广泛应用于事件监听和发布/订阅机制。使用观察者模式来实现事件驱动的编程,允许不同组件之间进行松耦合的通信。Spring中的事件(Event)是指应用程序中发生的某种特定状态的变化,比如应用启动、上下文刷新、HTTP请求处理等。当这些事件发生时,可以通过观察者模式将相关的监听器得到通知并做出响应。

  • Spring 容器在初始化和销毁时会发布相应的事件,比如 ContextRefreshedEvent 和 ContextClosedEvent。
  • Spring MVC 在处理请求过程中会发布 RequestHandledEvent 等事件。
  • Spring Data 在执行数据库操作时会发布 AfterSaveEvent 等事件。
  • 可以自定义事件和监听器,并将其注册到 Spring 容器中,实现自己的事件驱动逻辑。通过观察者模式,Spring 为我们提供了一种非常灵活和可扩展的编程模型。

四、总结优缺点以及使用经验

一、优点

  1. 解耦合:观察者模式将主题和观察者解耦,主题不需要知道观察者的具体实现,只需要维护一个观察者列表并通知它们。
  2. 可扩展性好:可以方便地添加新的观察者,而无需修改主题代码。
  3. 支持广播通信:主题可以向多个观察者对象发送通知,实现一对多的通信模式。

二、缺点

  1. 有可能出现循环依赖的问题:如果单个观察者对象被注册到一个或多个主题对象中,当主题状态发生变化时,可能会导致循环调用,使程序陷入死循环。
  2. 误操作风险:观察者需要正确地维护主题和观察者之间的注册关系,否则可能会出现观察者无法接收到通知或接收到重复通知的情况。

三、使用经验

  1. 适当使用:观察者模式适用于主题和观察者之间存在一对多的依赖关系,且需要实现自动通知机制的场景。但如果关系是一对一的,或者通知机制不是必需的,则不需要使用观察者模式,可以考虑使用其他设计模式。
  2. 维护注册关系:注意正确地注册和移除观察者,防止出现观察者无法接收到通知或接收到重复通知的情况。
  3. 避免循环依赖:如果存在循环依赖的风险,需要在设计时加以考虑,避免出现死循环的情况。
  4. 合理分配职责:主题负责维护观察者列表和通知,观察者负责响应通知并执行相应的操作。不要在主题或观察者中加入过多的逻辑,保持职责分离。
  5. 考虑性能:如果观察者数量较多,通知所有观察者可能会带来一定的性能开销。在必要时,可以考虑使用异步通知或者引入事件队列等机制来优化性能。
  • 15
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java语录精选

你的鼓励是我坚持下去的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值