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

相关推荐
JH30737 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
Coder_Boy_8 小时前
技术让开发更轻松的底层矛盾
java·大数据·数据库·人工智能·深度学习
invicinble8 小时前
对tomcat的提供的功能与底层拓扑结构与实现机制的理解
java·tomcat
较真的菜鸟8 小时前
使用ASM和agent监控属性变化
java
黎雁·泠崖8 小时前
【魔法森林冒险】5/14 Allen类(三):任务进度与状态管理
java·开发语言
qq_124987075310 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_10 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
Mr_sun.10 小时前
Day06——权限认证-项目集成
java
瑶山10 小时前
Spring Cloud微服务搭建四、集成RocketMQ消息队列
java·spring cloud·微服务·rocketmq·dashboard
abluckyboy10 小时前
Java 实现求 n 的 n^n 次方的最后一位数字
java·python·算法