观察者模式和发布订阅模式对比,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等,它们提供了更强大、更灵活的功能,可以满足不同场景的需求。

相关推荐
咖啡Beans1 分钟前
SpringBoot+Ehcache使用示例
java·spring boot
自由的疯3 分钟前
Java 使用Jackson进行深拷贝:优化与最佳实践
java·后端·架构
毕设源码-郭学长8 分钟前
【开题答辩全过程】以 springboot+美食电子商城的设计与实现为例,包含答辩的问题和答案
java·eclipse·美食
王嘉俊9259 分钟前
Kafka 和 RabbitMQ 使用:消息队列的强大工具
java·分布式·中间件·kafka·消息队列·rabbitmq·springboot
渣哥13 分钟前
事务没生效还以为成功了?Spring 事务失效的雷区你中招了吗?
java
教游泳的程序员1 小时前
【JDBC】系列文章第一章,怎么在idea中连接数据库,并操作插入数据?
java·ide·mysql·intellij-idea
懒羊羊不懒@1 小时前
C语言指针进阶(进阶)
java·开发语言·面试
nlog3n1 小时前
分布式秒杀系统设计方案
java·分布式
间彧1 小时前
JWT(JSON Web Token)详解
java
前路不黑暗@1 小时前
Java:代码块
java·开发语言·经验分享·笔记·python·学习·学习方法