本文字数:4159字,阅读大约需要 15 分钟。
观察者模式(Observer Pattern)是一种常见的行为型设计模式,用于在对象之间建立一种一对多的依赖关系。当一个对象的状态发生变化时,所有依赖它的对象都将得到通知并自动更新。
观察者模式在许多应用中都有广泛的应用,特别是当存在对象之间的一对多关系,并且需要实时通知和更新时,观察者模式非常适用。下面列举几个典型的使用场景:
以上仅是观察者模式的一些典型使用场景,实际上,只要存在对象之间的依赖关系,并且需要实现解耦和灵活性,观察者模式都可以考虑作为一种设计方案。
观察者模式包含以下几个核心角色:
下面是观察者模式的经典实现方式:
Observer
的接口,包含一个用于接收通知的方法,例如 update()
。public interface Observer {
void update();
}
Subject
的接口,包含用于管理观察者的方法,如 registerObserver()
、removeObserver()
和 notifyObservers()
。public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
Subject
接口,实现注册、移除和通知观察者的方法。在状态变化时调用 notifyObservers()
方法通知所有观察者。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();
}
}
}
Observer
接口,实现接收通知并进行相应处理的方法。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");
}
}
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语言中的地位非常重要。在JDK的 java.util 包中,提供 Observable
类以及 Observer
接口,它们构成了Java语言对观察者模式的支持。
使用 Observable
类以及 Observer
接口,优化之后的代码为:
// 具体观察者
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);
}
}
}
// 被观察者,继承 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;
}
}
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 中使用 Event Bus 来实现对观察者模式的支持。
com.google.common.eventbus.EventBus 提供了以下主要方法:
这些方法提供了事件的注册、注销、发布和获取监听器等功能,使得开发者可以方便地使用 EventBus 进行事件驱动编程。
@Getter
@AllArgsConstructor
public class MyEvent {
private String message;
}
@Slf4j
public class EventSubscriber {
@Subscribe
public void handleEvent(MyEvent event) {
String message = event.getMessage();
// Handle the event logic
log.info("Received event: " + message);
}
}
@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 Event 来实现观察者模式。
在Spring Event中,有一些核心的概念和组件,包括ApplicationEvent、ApplicationListener、ApplicationContext和ApplicationEventMulticaster。
通过使用这些关键概念和组件,可以在 Spring 应用程序中实现事件驱动的编程模型。事件发布者(ApplicationEventPublisher)可以发布特定类型的事件,而订阅者(ApplicationListener)可以监听和处理已发布的事件。ApplicationContext作为容器,负责管理事件和监听器,并使用ApplicationEventMulticaster来实现事件的广播和分发。
下面是使用 Spring Event 实现观察者模式的例子:
/**
* <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;
}
}
这里定义了一个基础事件发布抽象类,所有的事件发布类都可以继承此类。
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer userId;
private String userName;
}
public class UserEvent extends BaseEvent<User>{
private static final long serialVersionUID = 8145130999696021526L;
public UserEvent(Object source, User user) {
super(source,user);
}
}
@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);
}
}
@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 在实现观察者模式时具有以下优点:
@EventListener
注解,开发人员可以轻松定义事件监听器方法,并且不需要显式注册和注销监听器。观察者模式有以下几个优点:
虽然观察者模式具有许多优点,但也存在一些缺点:
综上所述,观察者模式在许多场景下都非常有用,但在使用时需要注意性能问题、循环依赖和执行顺序等方面的考虑。
希望这篇文章能给你带来收获和思考,如果你也有可借鉴的经验和深入的思考,欢迎评论区留言讨论。如果本文对你有帮助,请帮忙点个在看或者点个赞👍🏻。