【观察者模式】设计模式系列: 实现与最佳实践案例分析

news2024/11/15 10:31:38

文章目录

  • 观察者模式深入解析:在Java中的实现与应用
    • 1. 引言
      • 1.1 观察者模式简介
      • 1.2 模式的重要性及其在现实世界的应用示例
      • 1.3 本文的目标和读者定位
    • 2. 观察者模式的基本概念
      • 2.1 定义与原理
      • 2.2 UML类图和时序图
      • 2.3 核心原则
      • 2.4 使用场景
    • 3. 观察者模式与其他模式的关系
      • 3.1 与工厂模式
      • 3.2 与策略模式
      • 3.3 与组合模式
    • 4. 观察者模式的实现
      • 4.1 Java内置支持
      • 4.2 自定义实现
      • 4.3 代码示例
    • 5. 高级话题
      • 5.1 异步观察者模式
      • 5.2 线程安全问题
      • 5.3 性能考量
    • 6. 案例研究
      • 6.1 股票报价系统
      • 6.2 天气预报系统
    • 7. 观察者模式的变体
      • 7.1 事件监听器模式
      • 7.2 发布-订阅模式
      • 7.3 其他相关模式
    • 8. 实战经验分享
      • 8.1 常见错误与陷阱
      • 8.2 调试技巧
      • 8.3 重构建议
    • 9. 总结与展望

观察者模式深入解析:在Java中的实现与应用


1. 引言

1.1 观察者模式简介

观察者模式是一种软件设计模式,它定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并被自动更新。这种模式属于行为型设计模式之一,主要应用于实现发布-订阅机制。

1.2 模式的重要性及其在现实世界的应用示例

观察者模式的重要性在于它能够简化对象之间的交互逻辑,使得各个对象更加独立。在现实世界中,这种模式的应用十分广泛:

  • 新闻系统:当新闻发布时,所有订阅该新闻的用户会收到更新通知。
  • 股市行情:投资者关注特定股票的价格变动,价格变动时会及时通知到所有关注者。
  • 操作系统通知中心:应用程序可以注册成为某个事件的通知接收者,例如电池电量低时的通知。

1.3 本文的目标和读者定位

本文旨在详细介绍观察者模式的概念、原理以及在Java中的实现方法。此外,还将探讨观察者模式与其他几种设计模式的关系,以及如何在实际项目中应用这些模式。本教程适合已经了解基本面向对象编程概念的Java开发者阅读。


2. 观察者模式的基本概念

2.1 定义与原理

1. 什么是观察者模式?
观察者模式是一种行为设计模式,它允许一个对象(称为主题或被观察者)在状态发生变化时通知所有已注册的观察者对象,而无需知道这些观察者是谁。这种解耦方式使得主题和观察者都可以独立地发展而不影响彼此。

2. 模式的参与者角色介绍

  • Subject (主题):持有状态的对象,负责向观察者发送更新通知。
  • ConcreteSubject (具体主题):实现了Subject接口,维护一个观察者列表,并在状态变化时通知它们。
  • Observer (观察者):定义了一个更新接口,以便主题可以通知观察者。
  • ConcreteObserver (具体观察者):实现Observer接口,根据主题的状态更新自身的行为。

2.2 UML类图和时序图

在这里插入图片描述在这里插入图片描述

2.3 核心原则

1. 抽象耦合
观察者模式的核心在于实现抽象耦合,即主题和观察者之间通过接口进行通信,而不是直接引用具体的实现。这样做的好处是可以增加系统的灵活性和可扩展性。

2. 推送与拉取数据
观察者模式有两种常见的数据传递方式:

  • 推送数据:主题在通知观察者时直接传递更新后的数据。
  • 拉取数据:观察者在接收到更新通知后,主动从主题中获取最新的数据。

2.4 使用场景

1. 动态维护观察关系的例子
例如,在一个新闻系统中,用户可以订阅不同的新闻类别。当新闻类别中有新的新闻时,系统会自动通知所有订阅了该类别的用户。

2. 观察者模式适用的典型场景

  • UI组件:例如按钮点击事件,当按钮被点击时,触发一系列相关的UI更新。
  • 数据绑定:当模型数据发生变化时,视图自动更新。
  • 缓存管理:当缓存中某些数据过期时,自动更新缓存中的数据。

3. 观察者模式与其他模式的关系

3.1 与工厂模式

观察者模式通常不直接与工厂模式相结合,但在某些情况下,可以使用工厂模式来创建观察者对象。例如,可以有一个工厂类用于根据不同的需求创建不同类型的观察者。

3.2 与策略模式

策略模式允许算法的动态替换。观察者模式可以与策略模式结合使用,以提供不同的观察策略。例如,一个主题可以根据不同的情况选择不同的观察者行为。

3.3 与组合模式

组合模式用来构建树形结构以表示“部分-整体”的层次结构。观察者模式可以与组合模式结合使用,以创建复杂的观察者网络。例如,一个主题可以有多个层级的观察者,这些观察者按照一定的层次结构组织起来。


接下来是第四和第五部分的内容草稿:


4. 观察者模式的实现

4.1 Java内置支持

1. java.util.Observable
java.util.Observable 是Java标准库中提供的一个实现观察者模式的类。它实现了观察者模式的主题部分,并提供了添加观察者、移除观察者以及通知观察者的方法。

2. 主要方法

  • addObserver(Observer o): 添加一个观察者。
  • deleteObserver(Observer o): 移除一个观察者。
  • notifyObservers(): 通知所有的观察者。
  • notifyObservers(Object arg): 通知所有的观察者,并传递参数。

3. java.util.Observer 接口
java.util.Observer 是Java标准库中提供的一个接口,它定义了一个方法 update(Observable o, Object arg),该方法用于接收来自主题的通知。

4. 主要方法

  • update(Observable o, Object arg): 当主题发生变化时调用此方法,o 是主题对象,arg 是可选的数据。

4.2 自定义实现

1. 主题接口与具体主题类
在自定义实现中,我们可以定义自己的主题接口和具体主题类。

主题接口

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

具体主题类

public class ConcreteSubject implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private int state;

    @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(this);
        }
    }

    public void setState(int state) {
        this.state = state;
        notifyObservers();
    }

    public int getState() {
        return state;
    }
}

2. 观察者接口与具体观察者类
同样,我们也可以定义自己的观察者接口和具体观察者类。

观察者接口

public interface Observer {
    void update(Subject subject);
}

具体观察者类

public class ConcreteObserver implements Observer {
    private String name;
    private ConcreteSubject subject;

    public ConcreteObserver(String name, ConcreteSubject subject) {
        this.name = name;
        this.subject = subject;
    }

    @Override
    public void update(Subject subject) {
        System.out.println(name + " received: " + ((ConcreteSubject) subject).getState());
    }
}

4.3 代码示例

1. 创建主题

ConcreteSubject subject = new ConcreteSubject();

2. 注册观察者

ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);

subject.registerObserver(observer1);
subject.registerObserver(observer2);

3. 通知观察者

subject.setState(10); // 这将触发通知

4. 示例运行结果分析
setState() 方法被调用时,所有的观察者都将被通知。输出结果类似于:

Observer 1 received: 10
Observer 2 received: 10

5. 高级话题

5.1 异步观察者模式

1. 异步通知机制
异步观察者模式是指在通知观察者时使用非阻塞的方式。这种方式可以避免在通知大量观察者时导致主线程阻塞。

2. 实现细节
为了实现异步通知,可以使用线程池来执行通知操作,或者使用回调机制来异步地更新观察者。

5.2 线程安全问题

1. 并发访问的处理
由于观察者模式涉及到多个线程的并发访问,因此需要确保线程安全。如果不正确地处理并发访问,可能会导致数据不一致或程序崩溃等问题。

2. 线程安全的解决方案

  • 同步块: 使用 synchronized 关键字来同步对共享资源的访问。
  • 使用工具类: 如 ConcurrentHashMapCopyOnWriteArrayList 等线程安全的集合类。
  • 显式锁: 使用 ReentrantLockReadWriteLock 来控制对资源的访问。

5.3 性能考量

1. 性能优化技巧

  • 减少不必要的通知: 只有当状态真正发生变化时才通知观察者。
  • 使用缓存: 对于频繁查询的数据,可以使用缓存来减少计算成本。
  • 限制观察者的数量: 减少过多的观察者,以降低通知开销。

2. 最佳实践

  • 定期清理观察者列表: 避免不再需要的观察者仍然存在于列表中。
  • 合理选择数据传输方式: 根据实际情况选择推送或拉取数据。
  • 考虑使用第三方库: 如使用 Spring Framework 的事件传播机制。

6. 案例研究

6.1 股票报价系统

1. 系统架构概览
股票报价系统是一个典型的使用观察者模式的应用场景。在这个系统中,客户端(观察者)订阅感兴趣的股票信息,服务器端(主题)则负责收集股票价格的实时数据,并在数据发生变化时通知所有订阅的客户端。

系统组件

  • 服务器端: 收集和存储股票价格数据。
  • 客户端: 显示股票价格信息,订阅特定股票的价格更新。

2. 关键代码片段
服务器端(主题)实现

public class StockPriceService implements Subject {
    private Map<String, Double> stockPrices = new HashMap<>();
    private List<Observer> 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(this);
        }
    }

    public void setStockPrice(String stockSymbol, double price) {
        stockPrices.put(stockSymbol, price);
        notifyObservers();
    }

    public Map<String, Double> getStockPrices() {
        return stockPrices;
    }
}

客户端(观察者)实现

public class StockPriceDisplay implements Observer {
    private StockPriceService service;
    private Map<String, Double> stockPrices;

    public StockPriceDisplay(StockPriceService service) {
        this.service = service;
        this.stockPrices = new HashMap<>();
        service.registerObserver(this);
    }

    @Override
    public void update(Subject subject) {
        if (subject instanceof StockPriceService) {
            StockPriceService stockPriceService = (StockPriceService) subject;
            stockPrices = stockPriceService.getStockPrices();
            displayStockPrices();
        }
    }

    private void displayStockPrices() {
        stockPrices.forEach((symbol, price) -> System.out.println("Stock " + symbol + ": " + price));
    }
}

3. 测试与调试
为了确保系统正常工作,可以通过单元测试来验证服务器端和客户端的功能是否正确实现。此外,还可以设置断点进行调试,检查数据流是否符合预期。

6.2 天气预报系统

1. 设计思路
天气预报系统是一个实时监测天气变化并通知用户的系统。它包含一个气象站(主题),负责收集天气数据,以及多个客户端(观察者),用于显示天气信息。

2. 代码实现
气象站(主题)实现

public class WeatherStation implements Subject {
    private double temperature;
    private List<Observer> 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(this);
        }
    }

    public void setTemperature(double temperature) {
        this.temperature = temperature;
        notifyObservers();
    }

    public double getTemperature() {
        return temperature;
    }
}

客户端(观察者)实现

public class TemperatureDisplay implements Observer {
    private WeatherStation weatherStation;
    private double temperature;

    public TemperatureDisplay(WeatherStation weatherStation) {
        this.weatherStation = weatherStation;
        weatherStation.registerObserver(this);
    }

    @Override
    public void update(Subject subject) {
        if (subject instanceof WeatherStation) {
            WeatherStation station = (WeatherStation) subject;
            temperature = station.getTemperature();
            displayTemperature();
        }
    }

    private void displayTemperature() {
        System.out.println("Current temperature: " + temperature + "°C");
    }
}

3. 运行效果展示
当气象站的温度发生变化时,所有注册的客户端将立即更新显示的温度值。


7. 观察者模式的变体

7.1 事件监听器模式

1. 与观察者模式的区别
事件监听器模式与观察者模式相似,但更侧重于事件的处理。在事件监听器模式中,事件源(类似于主题)产生事件,而事件监听器(类似于观察者)响应这些事件。

2. 事件驱动编程
事件驱动编程是一种编程范式,其中程序的执行由外部事件驱动,而不是按预定的顺序执行。这种模式非常适合GUI应用程序和Web应用。

7.2 发布-订阅模式

1. 发布者与订阅者模型
发布-订阅模式是一种消息传递模式,其中发布者发送消息,订阅者接收消息。中间件(通常是消息总线)负责消息的路由。

2. 中间件的角色
中间件作为发布者和订阅者之间的桥梁,它可以是简单的消息队列,也可以是复杂的分布式消息系统。

7.3 其他相关模式

1. 命令模式
命令模式封装了一个请求作为对象,从而使你可用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

2. 责任链模式
责任链模式使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。


8. 实战经验分享

8.1 常见错误与陷阱

1. 错误处理

  • 空指针异常: 必须确保在调用观察者的方法之前检查它们是否已经被正确注册。
  • 内存泄漏: 如果观察者没有被正确地从主题中移除,可能会导致内存泄漏。

2. 内存泄漏

  • 确保观察者在不需要时被移除
  • 使用弱引用或软引用来管理观察者列表

8.2 调试技巧

1. 日志记录

  • 使用日志框架,如 Log4j 或 SLF4J,记录关键信息,以便于追踪问题。

2. 单元测试

  • 编写单元测试,确保观察者和主题之间的交互按预期工作。

8.3 重构建议

1. 重构时机

  • 当发现代码冗余或重复时
  • 当发现性能瓶颈时

2. 重构策略

  • 分离关注点,确保主题和观察者职责清晰。
  • 使用设计模式,如装饰者模式,来增强观察者的功能。

9. 总结与展望

1. 总结要点

  • 观察者模式的关键点回顾
    • 观察者模式是一种行为设计模式,用于实现一对多的依赖关系。
    • 主题和观察者通过接口进行通信,实现解耦。
    • 观察者模式可以应用于多种场景,如股票报价系统和天气预报系统。

2. 未来趋势

  • 观察者模式的发展方向
    • 随着异步编程和事件驱动架构的流行,观察者模式的应用将更加广泛。
    • 更高效的并发处理机制将被引入到观察者模式中。

3. 新兴技术的影响

  • 新兴技术,如微服务和容器化,将进一步推动观察者模式在分布式系统中的应用。

本文详细介绍了23种设计模式的基础知识,帮助读者快速掌握设计模式的核心概念,并找到适合实际应用的具体模式:
【设计模式入门】设计模式全解析:23种经典模式介绍与评级指南(设计师必备)

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

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

相关文章

【数据结构】Java实现链表

目录 链表的概念 链表的实现 链表的功能 框架搭建 功能实现 打印链表 获取数据数量 查询数据 插入数据 头插法 尾插法 指定位置插入 删除数据 删除一个数据 删除多个相同数据 删除链表 完整代码 链表的概念 链表是一种物理存储结构上非连续存储结构&#xff0…

nosql----redis三主三从集群部署

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…

【uniapp/uview1.x】解决在 u-popup 弹出层中使用 u-calendar 日历组件弹出方向出 bug 的问题

这个方法适用 uview 1.x 版本&#xff1b; 如果这个方法不适用可能是 uview 版本不一样&#xff0c;可以参考&#xff1a;https://github.com/dcloudio/uni-ui/issues/915 试试看 bug 的效果如图所示&#xff1a; 因为我为 popup 设置的方向为 top&#xff1a; <u-popup …

人工智能算法,图像识别技术;基于大语言模型的跨境商品识别与问答系统;图像识别

目录 一 .研究背景 二,大语言模型介绍 三,数据采集与预处理 商品识别算法 四. 跨境商品问答系统设计 五.需要源码联系 一 .研究背景 在当今全球化的背景下&#xff0c;跨境电商行业迅速发展&#xff0c;为消费者提供了更广泛的购物选择和更便利的购物方式。然而&#xf…

OLED屏幕制造工艺流程

OLED屏幕制造工艺流程是一个复杂且精细的过程&#xff0c;涉及多个关键步骤以确保最终的显示效果和性能。以下是OLED屏幕制造工艺流程的主要步骤&#xff1a; 1. 衬底制作与准备 材料选择&#xff1a;OLED器件需要一个透明的导电衬底&#xff0c;通常使用玻璃或塑料材料。 清…

集成RJ45网口网络变压器(网络隔离变压器)是如何影响网通设备的传输速率的。

华强盛电子导读RJ45连接器网口-199中间2643-0038 集成RJ45网口的网络变压器&#xff08;网络隔离变压器&#xff09;通常是指将网络变压器与RJ45连接器直接集成在一起的产品&#xff0c;这样的设计使得变压器可以直接安装在网络电缆的连接点上&#xff0c;而不需要额外的连接器…

【源码+文档+调试讲解】多媒体信息共享平台

摘 要 随着信息时代的来临&#xff0c;过去的武理多媒体信息共享管理方式缺点逐渐暴露&#xff0c;对过去的武理多媒体信息共享管理方式的缺点进行分析&#xff0c;采取计算机方式构建武理多媒体信息共享系统。本文通过阅读相关文献&#xff0c;研究国内外相关技术&#xff0c…

Unity | 游戏开发中的优化思维

目录 ​​​​​​一、优化三板斧&#xff1a; 第1步&#xff1a;定标准 第2步&#xff1a;重数据 第3步&#xff1a;严测试 二、流程和性能的优化 1.定标准&#xff1a; 2.重数据&#xff1a; 三、交互和表现的优化 1.卡顿和延迟 2.手感硬 四、沟通和学习 ​​​​…

C语言深度解析:static与extern关键字全指南

[大师C语言]合集&#xff3b;大师C语言(第一篇)&#xff3d;C语言栈溢出背后的秘密&#xff3b;大师C语言(第二十五篇)&#xff3d;C语言字符串探秘&#xff3b;大师C语言(第二篇)&#xff3d;C语言main函数背后的秘密&#xff3b;大师C语言(第二十六篇)&#xff3d;C语言结构体…

Electron 集成 Express + p-limit + SQlite WAL读写模式解决并发锁库的问题

背景 经过通信层面的优化后&#xff0c;我们不再走 Electron 提供的内置进程间通信 IPC&#xff0c;改为利用 Express 提供的 Http 本地服务来进行多处直达通信机制&#xff0c;同时利用本地 Sqlite 来保存大量数据&#xff0c;但 Express 提供的本地服务是支持并发请求的&…

食品零食小吃商城管理系统-计算机毕设Java|springboot实战项目

&#x1f34a;作者&#xff1a;计算机毕设残哥 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目、 源…

DataX二次开发之达梦数据库插件

达梦数据库自定义插件 达梦8的依赖引入定义reader module定义writer module修改核心配置数据库类型支持打包插件测试 以mysql到dm数据库为例配置mysql2dm.json执行任务查询下结果 DataX二次开发之达梦数据库插件 DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台,支持…

eNSP 华为远程登录路由器

华为远程登录路由器 前提&#xff1a;主机能与路由器通信就行&#xff0c;如果不同网段就配路由协议&#xff0c;这里直接模拟直连通信 Cloud&#xff1a; R&#xff1a; <Huawei>sys [Huawei]sys R [R]int g0/0/0 [R-GigabitEthernet0/0/0] [R-GigabitEthernet0/0/0]i…

AQS 原理详解

日常开发中&#xff0c;我们经常使用锁或者其他同步器来控制并发&#xff0c;那么它们的基础框架是什么呢&#xff1f;如何实现的同步功能呢&#xff1f;本文将详细讲解构建锁和同步器的基础框架--AQS&#xff0c;并根据源码分析其原理。 一、什么是 AQS&#xff1f; (一) AQS…

Oracle+ASM+High冗余详解及空间计算

Oracle ASM&#xff08;Automatic Storage Management&#xff09;的High冗余模式是一种提供高度数据保护的策略&#xff0c;它通过创建多个数据副本来确保数据的可用性和安全性。 以下是关于Oracle ASM High冗余的详细解释&#xff1a; 一、High冗余的特点 1.数据冗余度 在Hi…

Java | Leetcode Java题解之第342题4的幂

题目: 题解&#xff1a; class Solution {public boolean isPowerOfFour(int n) {return n > 0 && (n & (n - 1)) 0 && n % 3 1;} }

【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task03 大咖项目分享 人话八股文Bakwaan_Buddy项目开发尝试

【Datawhale AI夏令营第四期】 魔搭-大模型应用开发方向笔记 Task03 人话八股文Bakwaan_Buddy项目开发尝试 Idea: 我们草台班子目前的想法是解决大家计算机学院毕业面临的BUG——不爱背、背不下来八股文&#xff0c;觉得枯燥、烦、工作了用不着&#xff0c;反正就是知识他不进…

Python酷库之旅-第三方库Pandas(085)

目录 一、用法精讲 356、pandas.Series.str.isnumeric方法 356-1、语法 356-2、参数 356-3、功能 356-4、返回值 356-5、说明 356-6、用法 356-6-1、数据准备 356-6-2、代码示例 356-6-3、结果输出 357、pandas.Series.str.isdecimal方法 357-1、语法 357-2、参数…

RabbitMQ的核心概念

RabbitMQ是一个消息中间件&#xff0c;也是一个生产者消费者模型&#xff0c;负责接收&#xff0c;存储和转发消息。 核心概念 Producer 生产者&#xff0c;是RabbitMQ Server的客户端&#xff0c;向RabbitMQ发送消息。 Consumer 消费者&#xff0c;是RabbitMQ Server的客…

Ps:首选项 - 单位与标尺

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“单位与标尺” Units & Rulers选项卡允许用户根据工作需求定制 Photoshop 的测量单位和标尺显示方式。这对于保持工作的一致性和精确性&#xff0c;尤其是在跨设备或跨平台…