当前位置: 首页 > article >正文

观察者模式和订阅模式

观察者模式和订阅模式在概念上是相似的,它们都涉及到一个对象(通常称为“主题”或“发布者”)和多个依赖对象(称为“观察者”或“订阅者”)之间的关系。然而,尽管它们有相似之处,但在某些方面也存在细微的差别。

观察者模式(Observer Pattern)

‌核心思想‌

定义对象间的一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖于它的对象都会得到通知并自动更新。

‌结构‌

通常包括主题(Subject)和观察者(Observer)两个主要角色。主题维护一个观察者列表,当状态变化时,通知列表中的所有观察者。

‌实现方式‌

观察者模式可以通过在主题中维护一个观察者列表,并提供注册(addObserver)和注销(removeObserver)观察者的方法来实现。当主题状态变化时,遍历观察者列表并调用每个观察者的更新方法。

‌应用场景‌

常用于事件处理系统、GUI工具包中的事件监听器、订阅-发布系统等。

Demo

设计一个天气预报系统,当天气变化时,通知多个订阅了天气预报的用户。

定义 Subject 接口

Subject接口,被观察者,代表被观察的对象,定义注册新观察者,移除观察者,和通知观察者三个接口。

package org.example.observer;

import java.util.Observer;

public interface MySubject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

实现 Subject 接口

储存观察者列表,被观察对象变化信息,通知观察者列表

package org.example.observer;

import java.util.ArrayList;
import java.util.List;

public class WeatherStation implements MySubject {
    private List<MyObserver> observers;
    private String weather;

    public WeatherStation() {
        observers = new ArrayList<MyObserver>();
    }

    @Override
    public void registerObserver(MyObserver o) {
        this.observers.add(o);
    }

    @Override
    public void removeObserver(MyObserver o) {
        this.observers.remove(o);
    }

    @Override
    public void notifyObservers() {
        this.observers.forEach(observer -> observer.update(weather));
    }

    public void setWeather(String weather) {
        this.weather = weather;
        notifyObservers();
    }
}

定义 Observer 接口

Observer接口,代表观察者,定义接收到通知后需要执行的动作接口

package org.example.observer;

public interface MyObserver {
    void update(String weather);
}

实现 Observer 接口

观察者接收到通知后,实现具体要执行的动作

package org.example.observer;

public class User implements MyObserver {
    private String name;

    public User(String name) {
        this.name = name;
    }

    @Override
    public void update(String weather) {
        System.out.println(String.format("name %s receive weather update : %s", name, weather));
    }
}

测试

package org.example.observer;

public class MyObserverMain {
    public static void main(String[] args) {
        // 创建被观察者对象
        WeatherStation station = new WeatherStation();

        // 创建观察者对象
        User userA = new User("A");
        User userB = new User("B");
        User userC = new User("C");

        // 注册观察者,并更新天气
        station.registerObserver(userA);
        station.registerObserver(userB);
        station.registerObserver(userC);
        station.setWeather("Sunny");

        // 移除部分观察者,再次更新天气
        station.removeObserver(userA);
        station.setWeather("Rainy");

    }
}

订阅模式(Subscription Pattern)

‌核心思想‌

也是一种一对多的关系,但更强调“订阅”的概念。订阅者订阅某个主题或频道,以接收该主题或频道发布的更新或消息。

‌结构‌

通常包括发布者(Publisher)、订阅者(Subscriber)和消息(Message)三个主要角色。订阅者通过订阅某个发布者来接收其发布的消息。

‌实现方式‌

订阅模式可以通过事件总线(Event Bus)、消息队列(Message Queue)或专门的订阅系统来实现。订阅者可以订阅特定的主题或频道,并接收该主题或频道发布的消息。

‌应用场景‌

广泛用于消息传递系统、事件驱动架构、分布式系统中的服务间通信等。

Demo

定义消息类

package org.example.publish_subscriber;

public class Message {
    private String content;

    public Message(String content) {
        this.content = content;
    }

    public String getContent() {
        return content;
    }
}

定义订阅者接口

package org.example.publish_subscriber;

public interface Subscriber {
    void receive(String message);
}

实现订阅者接口

package org.example.publish_subscriber;

public class User implements Subscriber{
    private String name;
    
    public User(String name) {
        this.name = name;
    }
    
    @Override
    public void receive(String message) {
        System.out.println(String.format("%s received message: %s", name, message));
    }
}

定义事件总线

package org.example.publish_subscriber;

import java.util.Map;
import java.util.concurrent.*;

public class EventBus {
    private final Map<Subscriber, BlockingQueue<Message>> subscriberQueues = new ConcurrentHashMap<>();
    private final ExecutorService executor = Executors.newCachedThreadPool();
    private volatile boolean running = true;

    public void subscriber(Subscriber subscriber) {
        BlockingQueue<Message> queue = new LinkedBlockingQueue<>();
        subscriberQueues.put(subscriber, queue);
        executor.submit(() -> {
            try {
                while (running || !queue.isEmpty()) {
                    Message message = queue.take();
                    subscriber.receive(message);
                }
            } catch (Exception e) {
                Thread.currentThread().interrupt();
            }
        });
    }

    public void publish(Message message) {
        subscriberQueues.values().forEach(queue -> {
            try {
                queue.put(message);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
    }

    public void shutdown() {
        running = false;
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }
}

测试

package org.example.publish_subscriber;

public class PubSubMain {
    public static void main(String[] args) throws InterruptedException {
        EventBus eventBus = new EventBus();

        User userA = new User("A");
        User userB = new User("B");
        User userC = new User("C");
        User userD = new User("D");

        eventBus.subscriber(userA);
        eventBus.subscriber(userB);
        eventBus.subscriber(userC);
        eventBus.subscriber(userD);

        for (int i = 0; i < 10; i++) {
            eventBus.publish(new Message(i + "Hello World!"));
            eventBus.publish(new Message(i + "Hello Shore!"));
        }
        Thread.sleep(10000);

        eventBus.shutdown();
    }
}

在上面的例子中,executor.submit 被用于提交订阅者线程的任务。每个订阅者都有一个对应的工作队列,当发布消息时,消息会被放入每个订阅者的工作队列中。订阅者线程会不断地从自己的工作队列中取出消息并处理。通过这种方式,实现了发布-订阅模式中的异步通信和消息分发

区别与联系

‌区别‌

观察者模式更侧重于对象间的依赖关系和状态变化的通知机制;而订阅模式更强调消息的传递和订阅-发布的关系。此外,观察者模式通常是在单个应用或系统内使用;而订阅模式可能涉及跨系统或跨网络的消息传递。

‌联系‌

两者都涉及到一个对象(主题/发布者)和多个依赖对象(观察者/订阅者)之间的关系,且都实现了某种形式的通知机制。在某些情况下,它们可以相互替代或结合使用。例如,在一个分布式系统中,可以使用订阅模式来实现不同服务之间的消息传递,而在服务内部则可以使用观察者模式来实现状态变化的通知和更新。


http://www.kler.cn/a/406130.html

相关文章:

  • 微知-ib_write_bw的各种参数汇总(-d -q -s -R --run_infinitely)
  • 【Redis】基于Redis实现秒杀功能
  • 深度学习3
  • Specification封装一个类实现链式调用
  • 基于RTEMS项目学习waf build system
  • 刷题-1122
  • 信创时代的数据库之路:2024 Top10 国产数据库迁移与同步指南
  • Excel表查找与引用函数、逻辑函数、财务函数
  • Claude3.5-Sonnet和GPT-4o怎么选(附使用链接)
  • m个数 生成n个数的所有组合 详解
  • 全面前端显示:鹅成熟与否识别
  • 深入理解 HTTP 请求头与请求体
  • PG的并行查询
  • 亲测解决Unpack operator in subscript requires Python 3.11 or newer
  • 本地可运行,jar包运行错误【解决实例】:通过IDEA的maven package打包多模块项目
  • java基础---反射
  • 综合练习--轮播图
  • ubuntu20.04中编译安装gcc 9.2.0
  • .net将List<实体1>的数据转到List<实体2>
  • Linux 常用命令大汇总
  • 【数论】莫比乌斯函数及其反演
  • 探索免费的Figma中文版:开启高效设计之旅
  • tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), PORT)); 解析
  • (超级详细!!!)解决“com.mysql.jdbc.Driver is deprecated”警告:详解与优化
  • xxl-job入门
  • 支持多种快充协议和支持多种功能的诱骗取电协议芯片