Java设计模式之观察者模式详解

news2025/6/6 9:09:11

一、观察者模式简介

观察者模式(Observer Pattern)是一种行为型设计模式它定义了对象之间的一对多依赖关系。当一个对象(主题)的状态发生改变时,所有依赖于它的对象(观察者)都会自动收到通知并更新。这种模式又称为发布-订阅模式,广泛应用于事件监听、消息推送、实时数据更新等场景。

1. 核心思想

观察者模式的核心是解耦。主题(Subject)不需要知道观察者(Observer)的具体实现,只需通过接口进行交互。观察者可以动态注册或取消订阅主题的状态变化,从而实现灵活的扩展性和维护性。


二、观察者模式的结构

观察者模式包含以下四个核心角色:

  1. Subject(主题/被观察者)
    定义注册、删除和通知观察者的接口,维护观察者列表。

  2. ConcreteSubject(具体主题)
    实现Subject接口,管理自身状态,并在状态变化时通知所有观察者。

  3. Observer(观察者)
    定义更新接口,接收主题通知。

  4. ConcreteObserver(具体观察者)
    实现Observer接口,根据主题的状态变化执行具体操作。


三、观察者模式的代码实现

1.简单聊天系统示例

⑴.Subject(主题接口)

// 主题接口
interface ChatRoom {
    void registerUser(User user);
    void removeUser(User user);
    void sendMessage(String message);
}

⑵.Observer(观察者接口)

// 观察者接口
interface User {
    void receiveMessage(String message);
}

⑶.ConcreteSubject(具体主题:聊天室)

// 具体主题:聊天室
class GroupChat implements ChatRoom {
    private List<User> users = new ArrayList<>();
    
    @Override
    public void registerUser(User user) {
        users.add(user);
        System.out.println(user + " 加入了聊天");
    }
    
    @Override
    public void removeUser(User user) {
        users.remove(user);
        System.out.println(user + " 离开了聊天");
    }
    
    @Override
    public void sendMessage(String message) {
        System.out.println("发送消息: " + message);
        for (User user : users) {
            user.receiveMessage(message);
        }
    }
}

⑷.ConcreteObserver(具体观察者:用户)

// 具体观察者:用户
class ChatUser implements User {
    private String name;
    
    public ChatUser(String name) {
        this.name = name;
    }
    
    @Override
    public void receiveMessage(String message) {
        System.out.println(name + " 收到消息: " + message);
    }
    
    @Override
    public String toString() {
        return name;
    }
}

⑸.主类演示

public class SimpleChatSystem {
    public static void main(String[] args) {
        //创建聊天室
        GroupChat chatroom = new GroupChat();
        //创建用户
        ChatUser zhangsan = new ChatUser("张三");
        ChatUser lisi = new ChatUser("李四");
        ChatUser wangwu = new ChatUser("王五");
        //注册用户到聊天室
        chatroom.registerUser(zhangsan);
        chatroom.registerUser(lisi);
        chatroom.registerUser(wangwu);
        //发送信息
        chatroom.sendMessage("大家好");
        //移除一个用户
        chatroom.removeUser(lisi);
        //再发送一条信息
        chatroom.sendMessage("lisi已经离开");
    }
}

运行结果:

⑹.代码解释

①.核心接口和类

  • ChatRoom 接口:定义了主题的三个核心方法:注册用户、移除用户和发送消息。

  • User 接口:定义了观察者的接收消息方法。

  • GroupChat 类:实现了 ChatRoom 接口,维护一个用户列表,并在有新消息时通知所有用户。

②.具体观察者

  • ChatUser 类:实现了 User 接口,当收到消息时会打印消息内容。

③.主类演示

SimpleChatSystem 类:创建了一个 GroupChat 对象和多个 ChatUser 对象,并模拟了消息的发送过程。每次发送消息时,所有注册的用户都会收到通知。

2.天气预报系统

假设我们有一个天气预报系统,当天气数据发生变化时,需要通知所有注册的显示设备。

首先,定义观察者接口:

// 观察者接口
public interface Observer {
    void update(float temperature, float humidity, float pressure);
}

接下来,定义主题接口:

// 主题接口
public interface Subject {
    void registerObserver(Observer o);
    void removeObserver(Observer o);
    void notifyObservers();
}

然后,实现具体主题类:

// 具体主题类
import java.util.ArrayList;
import java.util.List;

public class WeatherData implements Subject {
    private List<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

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

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

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

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature, humidity, pressure);
        }
    }

    public void measurementsChanged() {
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

最后,实现具体观察者类:

// 具体观察者类
public class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;
    private Subject weatherData;

    public CurrentConditionsDisplay(Subject weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    @Override
    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    public void display() {
        System.out.println("Current conditions: " + temperature 
            + "F degrees and " + humidity + "% humidity");
    }
}

现在,让我们使用这些类来测试我们的天气系统:

// 测试类
public class WeatherStation {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        
        CurrentConditionsDisplay currentDisplay = 
            new CurrentConditionsDisplay(weatherData);
        
        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }
}

四、Java 内置的观察者模式支持

Java 提供了内置的观察者模式支持,位于 java.util 包中,包括 Observable 类和 Observer 接口。使用 Java 内置的支持可以简化观察者模式的实现:

import java.util.Observable;
import java.util.Observer;

// 使用Java内置的Observable类
public class WeatherData extends Observable {
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {}

    public void measurementsChanged() {
        setChanged();
        notifyObservers();
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }
}

// 使用Java内置的Observer接口
public class CurrentConditionsDisplay implements Observer, DisplayElement {
    Observable observable;
    private float temperature;
    private float humidity;

    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    @Override
    public void update(Observable obs, Object arg) {
        if (obs instanceof WeatherData) {
            WeatherData weatherData = (WeatherData)obs;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }

    @Override
    public void display() {
        System.out.println("Current conditions: " + temperature 
            + "F degrees and " + humidity + "% humidity");
    }
}

五、观察者模式的优缺点

优点:

  • 实现了对象之间的松耦合,主题和观察者可以独立变化和复用。
  • 符合开闭原则,无需修改主题即可增加新的观察者。
  • 支持广播通信,主题可以将通知发送给所有注册的观察者。

缺点:

  • 如果观察者过多,通知所有观察者可能会影响性能。
  • 观察者可能不知道其他观察者的存在,导致调试困难。
  • 如果主题和观察者之间存在循环依赖,可能会导致系统崩溃。

六、观察者模式的应用场景

观察者模式在以下场景中非常有用:

  • 当一个对象的状态变化需要通知其他对象时。
  • 当一个对象需要通知其他对象,而又不希望与这些对象紧密耦合时。
  • 当一个对象的改变需要同时改变其他对象,而不知道具体有多少对象需要改变时。
  • 当一个抽象模型有两个方面,其中一个方面依赖于另一个方面时。

七、总结

观察者模式是一种非常实用的设计模式,它提供了一种对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。Java 提供了内置的支持,使得实现观察者模式变得更加简单。在实际开发中,观察者模式被广泛应用于各种场景,如 GUI 事件处理、消息队列、状态管理等。通过合理使用观察者模式,可以使代码更加灵活、可维护和可扩展。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2401480.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

freeRTOS 消息队列之一个事件添加到消息队列超时怎么处理

一 消息队列的结构框图 xTasksWaitingToSend‌&#xff1a;这个列表存储了所有因为队列已满而等待发送消息的任务。当任务尝试向一个已满的队列发送消息时&#xff0c;该任务会被挂起并加入到xTasksWaitingToSend列表中&#xff0c;直到队列中有空间可用‌&#xff0c; xTasksW…

Authpf(OpenBSD)认证防火墙到ssh连接到SSH端口转发技术栈 与渗透网络安全的关联 (RED Team Technique )

目录 &#x1f50d; 1. Authpf概述与Shell设置的作用 什么是Authpf&#xff1f; Shell设置为/usr/sbin/authpf的作用与含义 &#x1f6e0;️ 2. Authpf工作原理与防火墙绕过机制 技术栈 工作原理 防火墙绕过机制 Shell关联 &#x1f310; 3. Authpf与SSH认证及服务探测…

组合与排列

组合与排列主要有两个区别&#xff0c;区别在于是否按次序排列和符号表示不同。 全排列&#xff1a; 从n个不同元素中任取m&#xff08;m≤n&#xff09;个元素&#xff0c;按照一定的顺序排列起来&#xff0c;叫做从n个不同元素中取出m个元素的一个排列。当mn时所有的排列情况…

Apache Druid

目录 Apache Druid是什么&#xff1f; CVE-2021-25646(Apache Druid代码执行漏洞) Apache Druid是什么&#xff1f; Apache Druid是一个高性能、分布式的数据存储和分析系统。设计用于处理大量实时数据&#xff0c;并进行低延迟的查询。它特别适合用于分析大规模日志、事件数据…

使用深蓝词库软件导入自定义的词库到微软拼音输入法

我这有一个人员名单&#xff0c;把它看作一个词库&#xff0c;下面我演示一下如何把这个词库导入微软输入法 首先建一个text文件&#xff0c;一行写一个词条 下载深蓝词库 按照我这个配置&#xff0c;点击转换&#xff0c;然后在桌面微软输入法那右键&#xff0c;选择设置 点…

使用Node.js分片上传大文件到阿里云OSS

阿里云OSS的分片上传&#xff08;Multipart Upload&#xff09;是一种针对大文件优化的上传方式&#xff0c;其核心流程和关键特性如下&#xff1a; 1. ‌核心流程‌ 分片上传分为三个步骤&#xff1a; 初始化任务‌&#xff1a;调用InitiateMultipartUpload接口创建上传任务…

复变函数中的对数函数及其MATLAB演示

复变函数中的对数函数及其MATLAB演示 引言 在实变函数中&#xff0c;对数函数 ln ⁡ x \ln x lnx定义在正实数集上&#xff0c;是一个相对简单的概念。然而&#xff0c;当我们进入复变函数领域时&#xff0c;对数函数展现出更加丰富和复杂的性质。本文将介绍复变函数中对数函…

【Linux】Linux程序地址基础

参考博客&#xff1a;https://blog.csdn.net/sjsjnsjnn/article/details/125533127 一、地址空间的阐述 1.1 程序地址空间 下面的图片展示了程序地址空间的组成结构 我们通过代码来验证一下 int g_unval; int g_val 100;int main(int argc, char *argv[]);void test1() {i…

将图形可视化工具的 Python 脚本打包为 Windows 应用程序

前文我们已经写了一个基于python的tkinter库和matplotlib库的图形可视化工具。 基于Python的tkinter库的图形可视化工具&#xff08;15种图形的完整代码&#xff09;:基于Python的tkinter库的图形可视化工具&#xff08;15种图形的完整代码&#xff09;-CSDN博客 在前文基础上&…

无人机军用与民用技术对比分析

一、材料区别 军用无人机&#xff1a; 1. 高强度特种材料&#xff1a; 大量使用钛合金、碳纤维复合材料&#xff0c;兼顾轻量化与高强度&#xff0c;提升抗冲击性和隐身性能。 关键部件依赖进口材料。 2. 隐身涂层&#xff1a; 采用雷达吸波材料和低红外特征涂料&#xf…

刷leetcode hot100--矩阵6/1

1.螺旋矩阵【很久】6/1【感觉就是思路的搬运工&#xff0c;没完全理解】 54. 螺旋矩阵 - 力扣&#xff08;LeetCode&#xff09; 原来想 但是如果是奇数矩阵&#xff0c;遍历不到中间 解决思路&#xff1a; 用left,right,top,down标记/限定每次遍历的元素&#xff0c;每次从…

Docker轻松搭建Neo4j+APOC环境

Docker轻松搭建Neo4jAPOC环境 一、简介二、Docker部署neo4j三、Docker安装APOC插件四、删除数据库/切换数据库 一、简介 Neo4j 是一款高性能的 原生图数据库&#xff0c;采用 属性图模型 存储数据&#xff0c;支持 Cypher查询语言&#xff0c;适用于复杂关系数据的存储和分析。…

定制开发开源AI智能名片S2B2C商城小程序在无界零售中的应用与行业智能升级示范研究

摘要&#xff1a;本文聚焦无界零售背景下京东从零售产品提供者向零售基础设施提供者的转变&#xff0c;探讨定制开发开源AI智能名片S2B2C商城小程序在这一转变中的应用。通过分析该小程序在商业运营成本降低、效率提升、用户体验优化等方面的作用&#xff0c;以及其与京东AI和冯…

【大模型:知识图谱】--5.neo4j数据库管理(cypher语法2)

目录 1.节点语法 1.1.CREATE--创建节点 1.2.MATCH--查询节点 1.3.RETURN--返回节点 1.4.WHERE--过滤节点 2.关系语法 2.1.创建关系 2.2.查询关系 3.删除语法 3.1.DELETE 删除 3.2.REMOVE 删除 4.功能补充 4.1.SET &#xff08;添加属性&#xff09; 4.2.NULL 值 …

贪心算法应用:装箱问题(BFD算法)详解

贪心算法应用&#xff1a;装箱问题(BFD算法)详解 1. 装箱问题与BFD算法概述 1.1 装箱问题定义 装箱问题(Bin Packing Problem)是组合优化中的经典问题&#xff0c;其定义为&#xff1a; 给定n个物品&#xff0c;每个物品有大小wᵢ (0 < wᵢ ≤ C)无限数量的箱子&#xf…

编程技能:格式化打印05,格式控制符

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;编程技能&#xff1a;格式化打印04&#xff0c;sprintf 回到目录…

MPLAB X IDE ​软件安装与卸载

1、下载MPLAB X IDE V6.25 MPLAB X IDE | Microchip Technology 正常选Windows&#xff0c;点击Download&#xff0c;等待自动下载完成&#xff1b; MPLAB X IDE 一台电脑上可以安装多个版本&#xff1b; 2、安装MPLAB X IDE V6.25 右键以管理员运行&#xff1b;next; 勾选 I a…

windows编程实现文件拷贝

项目源码链接&#xff1a; 实现文件拷贝功能&#xff08;限制5GB大小&#xff09; 81c57de 周不才/cpp_linux study - Gitee.com 知识准备&#xff1a; 1.句柄 句柄是一个用于标识和引用系统资源&#xff08;如文件、窗口、进程、线程、位图等&#xff09;的值。它不是资…

[6-01-01].第12节:字节码文件内容 - 属性表集合

JVM学习大纲 二、属性表集合&#xff1a; 2.1.属性计数器&#xff1a; 2.2.属性表&#xff1a; 2.3.字节码文件组成5 -> 属性&#xff1a; 1.属性主要指的是类的属性&#xff0c;比如源码的文件名、内部类的列表等 2.4.字节码文件组成3 -> 字段&#xff1a; 1.字段中…

基于机器学习的水量智能调度研究

摘要&#xff1a;随着城市化进程的加速和水资源供需矛盾的日益突出&#xff0c;传统的水量调度模式因缺乏精准预测和动态调控能力&#xff0c;难以满足现代供水系统对高效性、稳定性和节能性的要求。本文针对供水系统中用水峰谷预测不准确、能耗高、供需失衡等核心问题&#xf…