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

相关推荐
野犬寒鸦33 分钟前
从零起步学习并发编程 || 第四章:synchronized底层源码级讲解及项目实战应用案例
java·服务器·开发语言·jvm·后端·学习·面试
!停34 分钟前
数据结构二叉树——堆
java·数据结构·算法
virus59459 小时前
悟空CRM mybatis-3.5.3-mapper.dtd错误解决方案
java·开发语言·mybatis
没差c10 小时前
springboot集成flyway
java·spring boot·后端
时艰.10 小时前
Java 并发编程之 CAS 与 Atomic 原子操作类
java·开发语言
编程彩机10 小时前
互联网大厂Java面试:从Java SE到大数据场景的技术深度解析
java·大数据·spring boot·面试·spark·java se·互联网大厂
笨蛋不要掉眼泪10 小时前
Spring Boot集成LangChain4j:与大模型对话的极速入门
java·人工智能·后端·spring·langchain
Yvonne爱编码11 小时前
JAVA数据结构 DAY3-List接口
java·开发语言·windows·python
像少年啦飞驰点、12 小时前
零基础入门 Spring Boot:从“Hello World”到可上线微服务的完整学习指南
java·spring boot·微服务·编程入门·后端开发
眼眸流转12 小时前
Java代码变更影响分析(一)
java·开发语言