观察者模式和发布订阅模式对比,Java示例

观察者模式与发布订阅模式对比及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");
    }
}

相关第三方开源库

观察者模式相关库

  1. Java内置Observer/Observable

    Java提供了java.util.Observerjava.util.Observable类,可以直接实现观察者模式。

  2. RxJava

    RxJava是基于观察者模式的响应式编程库,提供了强大的异步和事件处理能力:

    java 复制代码
    Flowable.just("Hello world").subscribe(System.out::println);
  3. Guava EventBus

    虽然Guava EventBus常被归类为发布订阅模式,但其实现更接近观察者模式的变体:

    java 复制代码
    @Subscribe
    public void handleEvent(MyEvent event) {
        // 处理事件
    }

发布订阅模式相关库

  1. Guava EventBus

    Guava提供了同步和异步(AsyncEventBus)两种事件总线实现:

    java 复制代码
    EventBus eventBus = new EventBus();
    eventBus.register(subscriber);
    eventBus.post(event);
  2. RxBus

    基于RxJava的事件总线,结合了RxJava的强大功能和事件总线的简单性:

    java 复制代码
    @RegisterBus
    public void onMessageEvent(MessageEvent event) {
        // 处理事件
    }
  3. Spring Event

    Spring框架提供了完整的事件发布订阅机制:

    java 复制代码
    @EventListener
    public void handleContextStart(ContextStartedEvent event) {
        // 处理事件
    }

总结

观察者模式和发布订阅模式都是处理对象间通信的有效方式,选择哪种模式取决于具体需求:

  • 需要简单、直接的通信时,选择观察者模式
  • 需要解耦、异步处理或跨系统通信时,选择发布订阅模式

Java生态中有多种库可以简化这些模式的实现,如RxJava、Guava EventBus等,它们提供了更强大、更灵活的功能,可以满足不同场景的需求。

相关推荐
spencer_tseng2 小时前
Redis + Nacos.bat
java·windows·dos
troyzhxu2 小时前
列表查询的 GraphQL —— 一行代码终结你的 if-else 地狱!
java·springboot·graphql
孫治AllenSun3 小时前
【DataX】生产环境搭建DataX集群案例
java·开发语言·jvm
好好沉淀4 小时前
@NotBlank(message = “{xxx}“) 注解中花括号的含义
java
fīɡЙtīиɡ ℡4 小时前
内存泄漏产生的原因
java·spring·servlet
码智社5 小时前
Java实现RSA密钥生成、加密解密、加签验签
java
wear工程师5 小时前
ThreadLocal 在线程池里为什么会串数据:别只会答内存泄漏
java·后端
Iruoyaoxh5 小时前
栈和队列~
java·开发语言
审小匠OpenCPAi5 小时前
银行流水核查怎么自动化?单边匹配、双向勾稽与图聚类异常检测的工程对比
java·前端·人工智能·python·审计