观察者模式和订阅模式

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

观察者模式(Observer Pattern)

‌核心思想‌

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

‌结构‌

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

‌实现方式‌

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

‌应用场景‌

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

Demo

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

定义 Subject 接口

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

java 复制代码
package org.example.observer;

import java.util.Observer;

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

实现 Subject 接口

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

java 复制代码
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接口,代表观察者,定义接收到通知后需要执行的动作接口

java 复制代码
package org.example.observer;

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

实现 Observer 接口

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

java 复制代码
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));
    }
}

测试

java 复制代码
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

定义消息类

java 复制代码
package org.example.publish_subscriber;

public class Message {
    private String content;

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

    public String getContent() {
        return content;
    }
}

定义订阅者接口

java 复制代码
package org.example.publish_subscriber;

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

实现订阅者接口

java 复制代码
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));
    }
}

定义事件总线

java 复制代码
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();
        }
    }
}

测试

java 复制代码
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 被用于提交订阅者线程的任务。每个订阅者都有一个对应的工作队列,当发布消息时,消息会被放入每个订阅者的工作队列中。订阅者线程会不断地从自己的工作队列中取出消息并处理。通过这种方式,实现了发布-订阅模式中的异步通信和消息分发

区别与联系

‌区别‌

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

‌联系‌

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

相关推荐
cpsvps_net14 小时前
美国服务器环境下Windows容器工作负载智能弹性伸缩
windows
甄超锋15 小时前
Java ArrayList的介绍及用法
java·windows·spring boot·python·spring·spring cloud·tomcat
cpsvps17 小时前
美国服务器环境下Windows容器工作负载基于指标的自动扩缩
windows
网硕互联的小客服20 小时前
Apache 如何支持SHTML(SSI)的配置方法
运维·服务器·网络·windows·php
etcix20 小时前
implement copy file content to clipboard on Windows
windows·stm32·单片机
许泽宇的技术分享21 小时前
Windows MCP.Net:基于.NET的Windows桌面自动化MCP服务器深度解析
windows·自动化·.net
非凡ghost1 天前
AMS PhotoMaster:全方位提升你的照片编辑体验
windows·学习·信息可视化·软件需求
mortimer1 天前
一次与“顽固”外部程序的艰难交锋:subprocess 调用exe踩坑实录
windows·python·ai编程
gameatp1 天前
从 Windows 到 Linux 服务器的全自动部署教程(免密登录 + 压缩 + 上传 + 启动)
linux·服务器·windows
穷人小水滴1 天前
在 windows 运行 flatpak 应用 (WSL)
linux·windows·ubuntu