- 观察者模式是一种行为设计模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。当主题对象发生变化时,它的所有观察者都会收到通知并更新。观察者模式常用于实现事件处理系统、发布-订阅模式等。
- 在项目中,我们使用RabbitMQ、Kafka、ActiveMQ等消息中间件来进行解耦、异步、广播、削峰 ,在设计模式中我们可以使用观察者模式也能达到解耦、异步的特点。下面我将介绍观察者模式。
观察者模式的主要角色
- Subject(主题):被观察的对象,包含对观察者的引用列表,并提供注册、移除和通知观察者的方法。
- Observer(观察者):定义一个更新接口,用于接收主题的通知。
- ConcreteSubject(具体主题):具体的主题,实现了主题接口,状态发生改变时通知所有注册的观察者。
- ConcreteObserver(具体观察者):具体的观察者,实现了观察者接口,响应主题的变化。
Java 实现观察者模式
下面是一个简单的 Java 观察者模式示例:
1. 定义观察者接口
java
public interface Observer {
void update(String message);// 处理业务逻辑
}
2. 定义主题接口
java
public interface Subject {
void registerObserver(Observer observer);// 添加观察者
void removeObserver(Observer observer);// 移除观察者
void notifyObservers();// 通知所有观察者事件
}
3. 实现具体主题
java
public class ConcreteSubject implements Subject{
private List<Observer> observers;
private String message;
public ConcreteSubject() {
this.observers = new ArrayList<>();
}
@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(message);
}
}
public void setMessage(String message) {
this.message = message;
notifyObservers();
}
}
4. 实现具体观察者
java
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
// 模拟处理业务逻辑
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
5.测试
java
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer observer1 = new ConcreteObserver("Observer 1");
Observer observer2 = new ConcreteObserver("Observer 2");
Observer observer3 = new ConcreteObserver("Observer 3");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.registerObserver(observer3);
System.out.println("----------------------向三个订阅者发送消息 First Message----------------------");
subject.setMessage("First Message");
System.out.println("----------------------向三个订阅者发送消息 Second Message----------------------");
subject.setMessage("Second Message");
System.out.println("----------------------依次一个订阅者并发送消息 Third Message-------------------------");
subject.removeObserver(observer2);
subject.setMessage("Third Message");
}
}
6.结果
7.分析
-
在这里我们可以看到,我们已经可以做到可以向多个订阅者发送消息,而只需要创建一个新的订阅者放入observers容器里面,从而达到解耦的目的。
-
而要达到异步 的功能,可以利用java的并发工具,比如线程、"
ExecutorService
"等。下面给出实现思路:
1.使用 ExecutorService:
使用 Executors.newCachedThreadPool() 创建一个可缓存的线程池。可以使用其他类型的线程池,如 FixedThreadPool 或 SingleThreadExecutor,根据具体需求选择。
2.异步通知:
在 notifyObservers 方法中,通过 executorService.submit() 提交任务,使得每个观察者的 update 方法在单独的线程中执行,达到了异步通知的效果。
3.资源管理:
在合适的时候调用 executorService.shutdown() 来关闭线程池,确保资源得到释放。通过这种方式,实现了观察者模式的异步通知,使得观察者的 update 方法可以并发执行,提高了效率,避免了通知过程中阻塞的问题。
观察者模式在框架中的应用
1. Java Swing
Java Swing 中的事件处理模型大量使用了观察者模式。按钮点击、窗口关闭等事件的处理都是通过事件监听器实现的。
2. Spring Framework
Spring 框架中也广泛使用了观察者模式,特别是在事件驱动的编程模型中。
- ApplicationEvent 和 ApplicationListener : Spring 提供了一个事件机制,通过
ApplicationEvent
和ApplicationListener
实现观察者模式。
java
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;
@Component
public class MyApplicationListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
System.out.println("Context Refreshed Event received!");
}
}
java
public class MyCustomEvent extends ApplicationEvent {
public MyCustomEvent(Object source) {
super(source);
}
}
@Component
public class MyCustomEventListener implements ApplicationListener<MyCustomEvent> {
@Override
public void onApplicationEvent(MyCustomEvent event) {
System.out.println("Custom Event received!");
}
}
// 发布事件
@Component
public class MyBean {
private final ApplicationEventPublisher publisher;
public MyBean(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void publishEvent() {
MyCustomEvent event = new MyCustomEvent(this);
publisher.publishEvent(event);
}
}
3. Google Guava
Google Guava 提供的 EventBus
是一种实现发布-订阅(Pub/Sub)模式的工具,它的实现也是采用设计模式中的观察者设计模式。,它广泛应用于解耦事件生产者和消费者。在 EventBus
中,事件生产者和事件消费者可以彼此独立地工作,彼此无需直接依赖。
观察者模式的优缺点
优点
- 解耦:观察者和主题之间是抽象耦合的,可以独立扩展观察者和主题。
- 动态订阅:观察者可以在运行时动态添加或移除。
- 灵活性:可以在不修改主题类的情况下增加新的观察者,实现了开闭原则。
缺点
- 可能引起性能问题:如果观察者较多,通知所有观察者可能会比较耗时。
- 可能导致内存泄漏:如果观察者没有及时被移除,可能导致内存泄漏。
使用场景
- 事件处理系统:GUI 事件监听,按钮点击事件。
- 发布-订阅系统:消息队列,订阅推送服务。
- 数据变化通知:数据模型变化通知视图更新,类似于 MVC 模式中的观察者角色