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

相关推荐
渣哥3 分钟前
Kafka消息丢失的3种场景,生产环境千万要注意
java
渣哥3 分钟前
ElasticSearch深度分页的致命缺陷,千万数据查询秒变蜗牛
java
Olrookie4 分钟前
XXL-JOB GLUE模式动态数据源实践:Spring AOP + MyBatis 解耦多库查询
java·数据库·spring boot
柯南二号21 分钟前
【Java后端】MyBatis-Plus 原理解析
java·开发语言·mybatis
又是努力搬砖的一年29 分钟前
SpringBoot中,接口加解密
java·spring boot·后端
:-)32 分钟前
idea配置maven国内镜像
java·ide·maven·intellij-idea
啊阿狸不会拉杆1 小时前
《算法导论》第 27 章 - 多线程算法
java·jvm·c++·算法·图论
用户802973565411 小时前
【水平:编写简单的SpringCloud】用一篇文章精通SpringCloud-1
java
蔡俊锋1 小时前
Javar如何用RabbitMQ订单超时处理
java·python·rabbitmq·ruby
天天摸鱼的java工程师2 小时前
Snowflake 雪花算法优缺点(Java老司机实战总结)
java·后端·面试