观察者模式
- 一、原理及通俗理解
- 二、股票项目中使用
- 三、Spring框架中使用观察者模式
- 四、总结优缺点以及使用经验
- 一、优点
- 二、缺点
- 三、使用经验
一、原理及通俗理解
观察者模式是一种行为设计模式,它定义了对象之间的一对多依赖关系,使得一个对象的状态发生变化时,其所有依赖者都会收到通知并自动更新。
观察者模式涉及两个主要角色:Subject(主题)和Observer(观察者)。
- Subject:维护一个观察者列表,提供注册和移除观察者的方法,并在自身状态发生变化时,通知所有已注册的观察者。
- Observer:对感兴趣的事物(Subject)的状态变化做出相应的反应。
就像订阅了某个博客的通知一样,当博客作者发布新文章时,所有订阅者都会收到通知。在这里,博客作者就是主题,而订阅者则是观察者。
二、股票项目中使用
需求描述:假设我们有一个股票交易系统,需要实现对股票价格变化的实时通知功能。当股票价格发生变化时,所有订阅该股票的用户应该能够接收到最新的股票价格信息。
- 定义主题接口(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);
}
- 定义观察者接口(Observer) 包含更新股票价格的方法。
public interface Observer {void update(String stockSymbol, double newPrice);
}
- 实现具体的主题(StockExchange)
StockExchange
类实现了Subject
接口,扮演股票交易所的角色。它维护了一个观察者列表、股票列表和股票价格列表。registerObserver
和removeObserver
方法用于注册和移除观察者。股票价格变化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<>();}//注册观察者@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}//移除观察者@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}//股票价格变化,通知所有已注册的观察者。@Overridepublic 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);}}
}
- 实现具体的观察者(InvestorClient)
InvestorClient
类实现了Observer
接口,扮演投资者的角色。它会在收到股票价格更新时打印出通知信息。
public class InvestorClient implements Observer {private String investorName;public InvestorClient(String investorName) {this.investorName = investorName;}//股票价格更新时打印出通知信息。@Overridepublic void update(String stockSymbol, double newPrice) {System.out.println("Investor " + investorName + " received update: " + stockSymbol + " new price is $" + newPrice);}
}
- 使用示例 在
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);}
}
- 输出: 模拟了几次股票价格更新,观察者会收到相应的通知,最后,我们移除了一个观察者,再次更新股票价格时,只有剩余的观察者会收到通知。
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框架中使用观察者模式
- 在 Spring 的事件机制中,主要涉及以下几个核心组件:
- ApplicationEvent: 事件的基类,扮演观察者模式中的主题(Subject)角色。
- ApplicationListener:事件监听器接口,扮演观察者(Observer)角色,用于处理事件。
- ApplicationEventMulticaster:事件广播器,负责将事件分发给注册的监听器。
- 主题角色,
ApplicationEvent
继承自EventObject
类,EventObject
就是观察者模式中的主题,它维护了一个 source 属性,表示事件源,即产生事件的对象。
public abstract class ApplicationEvent extends EventObject {// .../** Helper constructor for subclasses. */public ApplicationEvent(Object source) {super(source);}// ...
}
- 观察者角色,
ApplicationListener
接口扩展自EventListener
,它定义了一个onApplicationEvent
方法,用于处理传入的事件。每个事件监听器都需要实现这个接口,并编写处理逻辑。
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {void onApplicationEvent(E event);
}
ApplicationEventMulticaster
接口定义了multicastEvent
方法,用于将事件广播给所有注册的监听器。
public interface ApplicationEventMulticaster {void multicastEvent(ApplicationEvent event);void multicastEvent(ApplicationEvent event, @Nullable ResolvableType eventType);// ...
}
- Spring 提供了一个默认实现
SimpleApplicationEventMulticaster
,它维护了一个ApplicationListener
列表,在接收到事件时,会遍历这个列表,依次通知每个监听器。
public class SimpleApplicationEventMulticaster extends AbstractApplicationEventMulticaster {// ...@Overridepublic 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 为我们提供了一种非常灵活和可扩展的编程模型。
四、总结优缺点以及使用经验
一、优点
- 解耦合:观察者模式将主题和观察者解耦,主题不需要知道观察者的具体实现,只需要维护一个观察者列表并通知它们。
- 可扩展性好:可以方便地添加新的观察者,而无需修改主题代码。
- 支持广播通信:主题可以向多个观察者对象发送通知,实现一对多的通信模式。
二、缺点
- 有可能出现循环依赖的问题:如果单个观察者对象被注册到一个或多个主题对象中,当主题状态发生变化时,可能会导致循环调用,使程序陷入死循环。
- 误操作风险:观察者需要正确地维护主题和观察者之间的注册关系,否则可能会出现观察者无法接收到通知或接收到重复通知的情况。
三、使用经验
- 适当使用:观察者模式适用于主题和观察者之间存在一对多的依赖关系,且需要实现自动通知机制的场景。但如果关系是一对一的,或者通知机制不是必需的,则不需要使用观察者模式,可以考虑使用其他设计模式。
- 维护注册关系:注意正确地注册和移除观察者,防止出现观察者无法接收到通知或接收到重复通知的情况。
- 避免循环依赖:如果存在循环依赖的风险,需要在设计时加以考虑,避免出现死循环的情况。
- 合理分配职责:主题负责维护观察者列表和通知,观察者负责响应通知并执行相应的操作。不要在主题或观察者中加入过多的逻辑,保持职责分离。
- 考虑性能:如果观察者数量较多,通知所有观察者可能会带来一定的性能开销。在必要时,可以考虑使用异步通知或者引入事件队列等机制来优化性能。