观察者模式与发布订阅模式对比及Java实现
核心概念对比
观察者模式和发布订阅模式都是用于实现对象间通信的设计模式,但它们有显著区别:
特性 | 观察者模式 | 发布订阅模式 |
---|---|---|
耦合度 | 主题和观察者直接耦合 | 通过消息代理解耦 |
通信方式 | 通常是同步的 | 通常是异步的 |
关系 | 一对多 | 多对多 |
彼此知晓 | 观察者知道主题 | 发布者和订阅者彼此不知道 |
实现复杂度 | 相对简单 | 相对复杂 |
典型应用 | GUI事件处理 | 消息队列、事件总线 |
观察者模式适合组件间紧密耦合的场景,而发布订阅模式适合需要解耦、支持多种通信方式的分布式系统。
Java代码实现示例
观察者模式实现
java
import java.util.ArrayList;
import java.util.List;
// 主题接口
interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}
// 观察者接口
interface Observer {
void update(String message);
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
public void setState(String state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
@Override
public void removeObserver(Observer o) {
observers.remove(o);
}
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.update(state);
}
}
}
// 具体观察者
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);
}
}
// 使用示例
public class ObserverPatternDemo {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
Observer obs1 = new ConcreteObserver("Observer1");
Observer obs2 = new ConcreteObserver("Observer2");
subject.registerObserver(obs1);
subject.registerObserver(obs2);
subject.setState("New State");
subject.removeObserver(obs2);
subject.setState("Another State");
}
}
发布订阅模式实现
java
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
// 事件总线/消息代理
class EventBus {
private Map<String, List<Consumer<String>>> subscribers = new HashMap<>();
public void subscribe(String topic, Consumer<String> subscriber) {
subscribers.computeIfAbsent(topic, k -> new ArrayList<>()).add(subscriber);
}
public void unsubscribe(String topic, Consumer<String> subscriber) {
if (subscribers.containsKey(topic)) {
subscribers.get(topic).remove(subscriber);
}
}
public void publish(String topic, String message) {
if (subscribers.containsKey(topic)) {
subscribers.get(topic).forEach(sub -> sub.accept(message));
}
}
}
// 发布者
class Publisher {
private EventBus eventBus;
private String name;
public Publisher(EventBus eventBus, String name) {
this.eventBus = eventBus;
this.name = name;
}
public void publish(String topic, String message) {
System.out.println(name + " publishing to " + topic + ": " + message);
eventBus.publish(topic, message);
}
}
// 订阅者
class Subscriber {
private String name;
public Subscriber(String name) {
this.name = name;
}
public Consumer<String> createHandler() {
return message -> System.out.println(name + " received: " + message);
}
}
// 使用示例
public class PubSubPatternDemo {
public static void main(String[] args) {
EventBus eventBus = new EventBus();
Publisher pub1 = new Publisher(eventBus, "Publisher1");
Publisher pub2 = new Publisher(eventBus, "Publisher2");
Subscriber sub1 = new Subscriber("Subscriber1");
Subscriber sub2 = new Subscriber("Subscriber2");
// 订阅主题
eventBus.subscribe("topic1", sub1.createHandler());
eventBus.subscribe("topic2", sub2.createHandler());
eventBus.subscribe("topic1", sub2.createHandler());
// 发布消息
pub1.publish("topic1", "Hello Topic1!");
pub2.publish("topic2", "Hello Topic2!");
// 取消订阅
eventBus.unsubscribe("topic1", sub2.createHandler());
pub1.publish("topic1", "This message won't reach Subscriber2");
}
}
相关第三方开源库
观察者模式相关库
-
Java内置Observer/Observable
Java提供了
java.util.Observer
和java.util.Observable
类,可以直接实现观察者模式。 -
RxJava
RxJava是基于观察者模式的响应式编程库,提供了强大的异步和事件处理能力:
javaFlowable.just("Hello world").subscribe(System.out::println);
-
Guava EventBus
虽然Guava EventBus常被归类为发布订阅模式,但其实现更接近观察者模式的变体:
java@Subscribe public void handleEvent(MyEvent event) { // 处理事件 }
发布订阅模式相关库
-
Guava EventBus
Guava提供了同步和异步(AsyncEventBus)两种事件总线实现:
javaEventBus eventBus = new EventBus(); eventBus.register(subscriber); eventBus.post(event);
-
RxBus
基于RxJava的事件总线,结合了RxJava的强大功能和事件总线的简单性:
java@RegisterBus public void onMessageEvent(MessageEvent event) { // 处理事件 }
-
Spring Event
Spring框架提供了完整的事件发布订阅机制:
java@EventListener public void handleContextStart(ContextStartedEvent event) { // 处理事件 }
总结
观察者模式和发布订阅模式都是处理对象间通信的有效方式,选择哪种模式取决于具体需求:
- 需要简单、直接的通信时,选择观察者模式
- 需要解耦、异步处理或跨系统通信时,选择发布订阅模式
Java生态中有多种库可以简化这些模式的实现,如RxJava、Guava EventBus等,它们提供了更强大、更灵活的功能,可以满足不同场景的需求。