Java二十三种设计模式-观察者模式(15/23)

news2024/11/26 2:39:43

观察者模式:实现对象间的松耦合通知机制

引言

在当今的软件开发领域,设计模式已成为创建可维护、可扩展和可重用代码的基石。在众多设计模式中,观察者模式以其独特的能力,实现对象间的松耦合通信而脱颖而出。本文将深入探讨观察者模式,一种允许对象状态变更自动通知依赖对象的行为型设计模式。我们将从其基本定义入手,阐释为何在现代软件架构中观察者模式至关重要,包括它如何实现解耦、动态交互和广播通信。接下来,文章将详细介绍观察者模式的组成部分,并通过Java代码示例展示其实现。此外,将讨论观察者模式的使用场景、优点与缺点,以及与其他设计模式的比较,最终提供一系列最佳实践和建议,以指导读者在适合的场景下有效运用观察者模式。无论您是资深架构师还是初出茅庐的开发者,本文都将为您提供宝贵的洞见,帮助您在构建复杂系统时做出更明智的设计选择。

 基础知识,java设计模式总体来说设计模式分为三大类:

(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

第一部分:观察者模式概述

1.1 定义与用途

观察者模式的基本定义

观察者模式(Observer Pattern)是一种行为型设计模式,主要用于实现对象间的一对多通信。当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。

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

解释为何需要观察者模式

  • 解耦:观察者模式可以将观察者和主题之间的依赖关系降至最低,实现松耦合。
  • 动态交互:允许对象在运行时动态地添加或移除观察者,增加了系统的灵活性。
  • 广播通信:当主题对象状态发生变化时,可以自动通知所有注册的观察者,实现广播通信。

1.2 观察者模式的组成

主题(Subject)

  • 定义:主题是观察者模式的核心,它维护了一系列观察者对象,并在状态发生改变时通知它们。
  • 职责:提供注册、移除和通知观察者的方法。

观察者(Observer)

  • 定义:观察者是一个接口,定义了观察者对象接收通知时需要实现的方法。
  • 职责:在接收到主题的通知后,更新自己的状态。

具体主题(Concrete Subject)

  • 定义:具体主题实现了主题接口,包含状态信息,并在状态改变时通知观察者。
  • 职责:存储状态信息,并在状态变化时调用观察者对象的更新方法。

具体观察者(Concrete Observer)

  • 定义:具体观察者实现了观察者接口,根据主题的当前状态进行相应操作。
  • 职责:接收来自主题的通知,并根据通知更新自己的状态或执行特定行为。

客户端(Client)

  • 角色:创建具体的观察者和主题对象,并将观察者注册到主题中。
  • 职责:组织主题和观察者之间的关联,但不参与它们之间的通信。

角色之间的交互

  • 注册与移除:观察者可以注册到主题中,也可以从主题中移除。
  • 状态变更通知:当具体主题的状态发生变化时,它会通知所有注册的观察者。
  • 更新行为:具体观察者接收到通知后,根据主题的当前状态执行更新行为。

观察者模式通过定义清晰的主题和观察者角色,以及它们之间的交互方式,实现了对象间的松耦合和动态通信。在下一部分中,我们将通过Java代码示例来展示观察者模式的具体实现。

第二部分:观察者模式的实现

2.1 Java实现示例

以下是使用Java语言实现观察者模式的代码示例。在这个示例中,我们将创建一个天气监测系统,其中WeatherData是主题,负责通知观察者天气变化;CurrentConditionsDisplay是观察者,根据接收到的天气信息更新显示。

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

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

// 具体主题
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) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(i);
        }
    }

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

// 具体观察者
class CurrentConditionsDisplay implements Observer {
    @Override
    public void update(float temperature, float humidity, float pressure) {
        System.out.println("Current conditions: " +
                "Temp: " + temperature + "F, " +
                "Humidity: " + humidity + "%, " +
                "Pressure: " + pressure + " mb");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay currentConditionsDisplay = new CurrentConditionsDisplay();

        weatherData.registerObserver(currentConditionsDisplay);

        // 模拟天气数据变化
        weatherData.setMeasurements(80.6f, 65.0f, 30.4f);
    }
}

2.2 观察者模式中的角色和职责

观察者(Observer)

  • 职责:定义了一个更新接口,使得在主题状态改变时,观察者可以接收到通知。

主题(Subject)

  • 职责:维护观察者列表,提供注册和移除观察者的方法,并在状态改变时通知它们。

具体主题(Concrete Subject)

  • 职责:实现主题接口,存储状态信息,并在状态改变时通知观察者。

具体观察者(Concrete Observer)

  • 职责:实现观察者接口,根据接收到的状态信息执行相应的行为。

相互作用

  • 注册与移除:观察者可以注册到主题的观察者列表中,也可以从列表中移除。
  • 状态变更通知:主题在状态发生变化时,会遍历观察者列表并调用它们的更新方法。
  • 执行更新:观察者接收到通知后,根据主题的当前状态执行更新操作。

观察者模式允许对象间的松耦合,使得当一个对象状态发生变化时,所有依赖于它的对象都会得到通知。这种模式在需要实现事件驱动架构或发布-订阅模式的场景中非常有用。在下一部分中,我们将探讨观察者模式的使用场景。

第三部分:观察者模式的使用场景

3.1 需要对象间松耦合的场景

在软件设计中,对象间的松耦合是实现灵活、可维护系统的关键。观察者模式提供了一种机制,允许对象间的交互而不需要紧密耦合。

观察者模式的应用:

  • 事件驱动架构:在事件驱动的系统中,对象可能需要对某些事件做出响应,而不必显式依赖于事件的来源。
  • UI与业务逻辑分离:在图形用户界面编程中,界面组件(观察者)需要根据业务逻辑的变化(主题)更新显示,而不直接依赖于业务逻辑的具体实现。

应用实例:

  • 模型-视图-控制器(MVC):在MVC架构中,视图组件作为观察者,模型作为主题,当模型数据变化时,视图会自动更新。

3.2 需要自动更新多个依赖对象的场景

当系统中的一个对象状态发生变化时,可能需要自动更新所有依赖于该状态的对象。观察者模式通过主题与观察者的关系,实现了这种自动更新机制。

观察者模式的优势:

  • 自动更新:当主题对象状态变化时,所有注册的观察者都会自动收到通知并更新。
  • 减少手动管理:减少了手动更新每个依赖对象的需要,简化了代码逻辑。

应用实例:

  • 股票市场监控:在股票市场监控系统中,股票价格的变化(主题)需要实时更新所有监控器(观察者),如交易界面、数据分析工具等。

通过观察者模式,可以构建一个灵活的通信机制,使得对象之间的交互更加松散,同时确保了系统的高内聚性和低耦合性。这种模式在实际开发中非常有价值,尤其是在需要处理多个对象之间的复杂交互时。在下一部分中,我们将讨论观察者模式的优点与缺点。

第四部分:观察者模式的优点与缺点

4.1 优点

实现松耦合

  • 解耦对象:观察者模式允许对象之间的交互而无需紧密耦合,使得系统的组件更加独立。

提高系统的灵活性

  • 动态交互:可以在运行时动态地添加或移除观察者,使得系统能够灵活地响应变化。

广播通信

  • 状态变更通知:当主题的状态发生变化时,所有注册的观察者都会收到通知,实现广播通信。

易于扩展

  • 添加新观察者:可以轻松地添加新类型的观察者,而无需修改主题的代码。

易于维护

  • 分离关注点:将状态管理与状态变更的响应分离,使得各个部分更容易维护。

4.2 缺点

循环依赖

  • 观察者与主题:如果不当使用,可能会导致观察者和主题之间的循环依赖问题。

系统性能影响

  • 通知开销:在有大量观察者时,通知机制可能会带来性能开销。

顺序依赖

  • 执行顺序:观察者的执行顺序可能难以控制,有时这可能会导致问题。

过度使用

  • 滥用模式:在不需要观察者模式的场景中使用它,可能会导致系统设计过于复杂。

难以管理大量观察者

  • 观察者管理:当观察者数量很多时,管理这些观察者可能会变得困难。

通知不同步

  • 并发问题:在多线程环境中,观察者的更新可能需要同步处理,以避免并发问题。

观察者模式提供了一种强大的通信机制,使得对象之间的交互更加灵活和松耦合。然而,合理使用观察者模式并避免其缺点是至关重要的。了解其优点和缺点可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用观察者模式,以达到最佳的设计效果。

第五部分:观察者模式与其他模式的比较

5.1 与发布-订阅模式的比较

发布-订阅模式(Pub-Sub Pattern)

  • 定义:发布-订阅模式是一种消息通信模式,其中消息的发送者(发布者)和接收者(订阅者)之间没有直接的耦合关系,消息通过一个中介(如消息队列或事件总线)传递。
  • 特点:允许多个订阅者接收发布者的消息,支持一对多的消息传递。

观察者模式

  • 定义:观察者模式是一种对象行为模式,其中一个对象(主题)的状态改变会通知所有依赖于它的对象(观察者)。
  • 特点:主题和观察者之间存在直接的耦合,通常是一对一或一对多的关系。

对比

  • 耦合度:发布-订阅模式中的耦合度更低,因为消息通过中介传递,而观察者模式中主题需要显式地管理观察者列表。
  • 消息传递:发布-订阅模式可以处理更复杂的通信场景,如消息过滤、异步消息传递等,而观察者模式通常用于同步更新。
  • 使用场景:发布-订阅模式适用于大规模的分布式系统,观察者模式适用于对象间相对简单的通信。

5.2 与状态模式的对比

状态模式(State Pattern)

  • 定义:状态模式是一种行为型设计模式,允许对象在其内部状态改变时改变其行为,看起来像是改变了其类。
  • 特点:通过将每个状态封装为一个单独的类,状态转换逻辑由外部控制。

观察者模式

  • 定义:如前所述,观察者模式用于在对象间实现松耦合的通信,以便在状态变化时通知依赖对象。

对比

  • 状态管理:状态模式专注于对象状态的管理以及状态变化时的行为变化,而观察者模式专注于状态变化的通信机制。
  • 行为变化:状态模式允许对象根据当前状态改变其行为,观察者模式则不涉及对象行为的变化,只负责通知。
  • 使用场景:状态模式适用于对象状态复杂且状态变化影响行为的场景,观察者模式适用于需要在多个对象间同步状态的场景。

观察者模式和发布-订阅模式、状态模式都提供了处理对象状态变化和通信的不同方法。每种模式都有其独特的用途和优势,选择使用哪种模式取决于具体的设计需求和场景。在下一部分中,我们将提供观察者模式的最佳实践和建议。

第六部分:观察者模式的最佳实践和建议

6.1 最佳实践

确保接口定义清晰

  • 明确契约:确保主题和观察者之间的接口定义清晰,避免因接口模糊导致的错误。

保持主题和观察者的解耦

  • 减少依赖:设计主题时,避免对观察者的具体实现有所依赖。

使用组合优于继承

  • 组合关系:考虑使用组合关系来构建主题和观察者,以提高灵活性。

管理观察者列表

  • 动态注册与移除:提供清晰的注册和移除观察者的机制,确保观察者列表的准确性。

考虑线程安全

  • 并发控制:在多线程环境中使用观察者模式时,确保线程安全。

定义通知顺序

  • 有序通知:如果观察者的执行顺序重要,定义一个逻辑来控制通知的顺序。

提供取消订阅机制

  • 自主控制:允许观察者在不再需要接收通知时取消订阅。

6.2 避免滥用

避免过度使用

  • 合理使用:仅在对象间确实需要松耦合通信时使用观察者模式。

避免循环引用

  • 检查循环:避免因观察者模式导致的循环引用问题。

避免性能问题

  • 性能考量:在观察者众多的情况下,考虑通知机制可能带来的性能问题。

避免复杂化设计

  • 简化设计:不要因为使用观察者模式而使系统设计过于复杂。

6.3 替代方案

使用事件总线

  • 解耦通信:事件总线提供了一种更加解耦的方式来处理事件和消息传递。

使用消息队列

  • 异步处理:对于需要异步处理的场景,消息队列可以是一个合适的选择。

使用状态模式

  • 状态变化:当对象的状态变化需要改变其行为时,可以考虑使用状态模式。

使用命令模式

  • 命令封装:对于需要将操作封装为对象的场景,命令模式可以提供帮助。

使用责任链模式

  • 处理链:当一个请求需要沿着处理链传递时,责任链模式可以定义对象间的传递顺序。

观察者模式是一种强大的设计模式,可以在对象间实现松耦合的通信机制。合理使用观察者模式并避免其缺点对于构建灵活、可维护的系统至关重要。了解其替代方案可以帮助开发者根据具体需求和场景选择最合适的设计模式。在实际开发中,应根据具体情况灵活运用观察者模式,以达到最佳的设计效果。

结语

观察者模式是一种强大的设计模式,用于在对象间实现松耦合的通信机制。通过本文的深入分析,希望读者能够对观察者模式有更全面的理解,并在实际开发中做出合理的设计选择。

博主还写了其他Java设计模式关联文章,请各位大佬批评指正:

(一)创建型模式(5种):

Java二十三种设计模式-单例模式(1/23)

Java二十三种设计模式-工厂方法模式(2/23)

Java二十三种设计模式-抽象工厂模式(3/23)

Java二十三种设计模式-建造者模式(4/23)

Java二十三种设计模式-原型模式(5/23)

(二)结构型模式(7种): 

Java二十三种设计模式-适配器模式(6/23)

Java二十三种设计模式-装饰器模式(7/23)

Java二十三种设计模式-代理模式(8/23)

Java二十三种设计模式-外观模式(9/23)

Java二十三种设计模式-桥接模式(10/23)

Java二十三种设计模式-组合模式(11/23)

Java二十三种设计模式-享元模式(12/23)

 (三)行为型模式(11种): 

Java二十三种设计模式-策略模式(13/23)

Java二十三种设计模式-模板方法模式(14/23) 

未完待续,持续更新中...... 

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

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

相关文章

信息安全等级保护:全面解读与实践指南

一、什么是等级保护&#xff1f; 1.1 概念 信息安全等级保护是依据我国《信息安全等级保护管理办法》&#xff0c;对各类信息系统基于重要程度和保密需求进行分级&#xff0c;并制定相应技术与管理措施&#xff0c;以保障信息系统的安全性、完整性和可用性。等级保护共分五级…

【开源 Mac 工具推荐之 4】Awesome-macOS:全能的宝藏工具库

简介 Awesome-macOS 是一个开源项目&#xff0c;属于 GitHub 的热门项目“Awesome”的体系&#xff0c;旨在为 macOS 用户提供一个集合了各种优秀的 macOS 应用程序、插件、脚本和工具的精选列表。该项目由开源社区共同维护&#xff0c;通过不断收集和整理优秀的macOS资源&…

小白安装---Ubuntu教程!!!

1、首先将映像源放入到一个不被删除的地方。 2、打开vmware&#xff0c;创建一个虚拟机 3、 按照顺序点击 4、选择映像源 选择22版的映像 5、设置主机名称和密码 6、设置虚拟机名称和位置 7、 设置磁盘大小 8、选择配置&#xff0c;使用推荐的就行&#xff08;这个可以随时修改…

二级制安装LAMP

一、安装Apache 1.1解压 tar xf apr-1.6.2.tar.gz tar xf apr-util-1.6.0.tar.gz tar xf httpd-2.4.29.tar.bz2 mv apr-1.6.2 httpd-2.4.29/srclib/apr mv apr-util-1.6.0 httpd-2.4.29/srclib/apr-util1.2安装相关工具 yum -y install \ gcc \ #C语言的编译器 gcc-c+…

HarmonyOS应用一之登录页面案例

目录&#xff1a; 1、代码示例2、代码分析3、注解分析 1、代码示例 实现效果&#xff1a; /** Copyright (c) 2023 Huawei Device Co., Ltd.* Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance w…

【数据结构】六、图:6.图的最短路径(BFS 算法、迪杰斯特拉(Dijkstra)算法、弗洛伊德(Floyd)算法)

3.最短路径 文章目录 3.最短路径3.1 BFS 算法3.2 迪杰斯特拉(Dijkstra)算法3.3 弗洛伊德(Floyd)算法总结 在网图和非网图中&#xff0c;最短路径的含义是不同的。 由于非网图它没有边上的权值&#xff0c;所谓的最短路径&#xff0c;其实就是指两顶点之间经过的边数最少的路径。…

JAVA—集合框架

集合 大小可变 是存储数据的容器&#xff0c;本文是在学习过ArrayList集合后对于集合进行一个比较系统的学习&#xff0c;只要是对于Collection类和Map类的学习和案例实践。需要对于Stream流再进行加深学习和理解&#xff0c;功能比较强大和简洁。 目录 1.集合体系结构 &…

流程图语法Mermaid教程

在使用Markdown来编写博客的过程中&#xff0c;尤其是需要更醒目的逻辑说明时&#xff0c;就需要使用流程图。 既然CSDN官方推荐Mermaid作为流程图语法&#xff0c;那我也针对Mermaid来做一期教程。 在学习之前&#xff0c;先总结一下流程图的需求&#xff1a; 节点设置方向设…

好看的超清4K视频素材去哪里找?下载素材资源网站分享

在当前高清与4K视频素材盛行的时代&#xff0c;创作出色的视频内容离不开优质的超清4K视频素材。以下是一些宝藏网站&#xff0c;它们提供了丰富的4K视频素材&#xff0c;可以使您的视频创作更加引人注目。 蛙学网 蛙学网是获取高质量4K视频素材的首选之地。该网站详细标注了视…

lvs、集群

1.集群和分布式 当多个用户当用户访问一个服务器时&#xff0c;服务器server1可能就会崩&#xff0c;假如这时候我们新加一个服务器server2来缓解server1的压力&#xff0c;那么就需要一个调度器lvs来分配&#xff0c;所以现在就是用户的访问就需要通过调度器之后到达服务器&a…

期望薪资3k,面试官笑了但没说话

吉祥知识星球http://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247485367&idx1&sn837891059c360ad60db7e9ac980a3321&chksmc0e47eebf793f7fdb8fcd7eed8ce29160cf79ba303b59858ba3a6660c6dac536774afb2a6330#rd 《网安面试指南》http://mp.weixin.qq.com/s?…

Linux系统编程(8)进程进阶

一、进程的执行 子进程被创建好后&#xff0c;就需要去执行它所该执行的功能&#xff0c;根据子进程需要做的事&#xff0c;将其分为两类&#xff1a; 1.子进程所做的事与父进程差不多&#xff0c;两者功能几乎一样 //子承父业 2.子进程所做的事和父进程做的事完全不同&…

随手记录第十四话 -- 在 Spring Boot 3.2.3 中使用 springdoc-openapi-starter-webmvc-ui

项目升级到JDK21后&#xff0c;SpringBoot版本也到了3.2.3&#xff0c;之前的Swagger-ui不在支持了&#xff0c;找了其他的一直忘记记录了&#xff0c;这里记录一下。 快捷目录 1.引言2.添加依赖3.配置类4.Java代码实现5.启动应用6.总结 1.引言 随着 Spring Boot 版本的更新&a…

一个C++模板有意思的小实验

看面经遇到一个C模板的问题&#xff0c;顺手做了下实验看看结果&#xff0c;觉得比较有意思就记录一下 我们一般用模板会把声明和定义放在一起(放在同一个头文件内)&#xff0c;那么如果我们在一个头文件内声明我们要使用的模板函数&#xff0c;并在另一个cpp文件内实现会怎么样…

HarmonyOS NEXT星河版零基础入门(2)

1.Scroll滚动容器-核心用法 1&#xff08;1&#xff09;.Scroll的核心用法 快速得到一个长度为5的数组 Array.from({length:5}) 代码&#xff1a; 这种是默认是竖向的 要是想要实现横向的 就得把Scroll里边的Column改为Row组件 并且把scrolllable设置为Horizontal横向 1&a…

Spring AI 更新:支持OpenAI的结构化输出,增强对JSON响应的支持

就在昨晚&#xff0c;Spring AI发了个比较重要的更新。由于最近OpenAI推出了结构化输出的功能&#xff0c;可确保 AI 生成的响应严格遵守预定义的 JSON 模式。此功能显着提高了人工智能生成内容在现实应用中的可靠性和可用性。Spring AI 紧随其后&#xff0c;现在也可以对OpenA…

如何在萤石云视频app转移设备

本文将详述如何在萤石云视频app转移设备 一&#xff0e; 注意事项 1. 若设备正在分享中,无法转移 2. 若设备已开通云存储等可以转移的增值服务&#xff0c;相应的服务也会转移&#xff1b;若开通了订阅服务将取消订阅服务&#xff0c;次月不再扣费续订 3. 转移设备时若设备…

【iOS】—— Runloop和多线程问题总结

Runloop和多线程问题总结 runloop总结1. runloop简介2. runloop的基本作用3. 获取runloop的流程4. runloop和线程的关系5. runloop中的Mode有几种以及作用6.runloop的事件源7. 讲一下source0和source18. runloop的六种观察者模式9. 针对定时器在滑动时停止工作的问题10. 如何解…

【pytorch学习】transforms的使用

1 ToTensor() 作用&#xff1a;将 PIL Image 类型或者 numpy.ndarray 类型转为 tensor 类型 实例&#xff1a; from torchvision import transforms from PIL import Imageimg_path "data/train/bees/17209602_fe5a5a746f.jpg" img Image.open(img_path)print(i…

[数据结构]-快速排序

学习快排的基础 不想自己推导时间复杂度或者了解随机化的合理性,可以忽视下面这一条. 推导时间复杂度的方法:主定理随机化算法—数学基础:概率统计 QuickSort 快速排序: 1962年,Tony Hoare发明了这种如此高效实用的强大排序算法 分治法的体现:Divide and Conquer原地排序,…