03-JAVA设计模式-观察者模式

news2025/3/12 23:31:03

观察者模式

什么是观察者模式

Java中的观察者模式是一种常见的设计模式,它允许对象(观察者)订阅另一个对象(被观察者)的状态变化,并在状态变化时自动得到通知。

核心:
观察者模式主要用于1:N的通知。当一个对象(目标对象)的状态发生变化时,它需要及时告知一系列对象(观察者对象)令他们做出响应。

通知观察者的方式:

  • 推:
    • 每次都会把通知以广播的方式发送给所有观察者,所有观察者只能被动接收。
  • 拉:
    • 观察者只要知道有情况即可。至于什么时候获取内容,获取什么内容都可以自主决定。

优点:

  • 解耦:观察者模式降低了对象之间的耦合度,使得它们可以独立地改变和复用。
  • 扩展性:新的观察者可以很容易地加入到现有系统中,而无需修改其他部分的代码。
  • 灵活性:观察者模式允许在运行时动态地改变观察者和被观察者的关系。

缺点:

  • 开发和调试复杂:由于系统中存在多个观察者和被观察者,开发和调试可能会变得相对复杂。
  • 性能问题:如果观察者数量庞大,或者被观察者状态改变频繁,可能会导致性能下降。
  • 消息通知顺序:在Java中,消息的通知一般是顺序执行的,一个观察者卡顿可能会影响整体的执行效率。

常见场景:

  • 图形用户界面(GUI)开发:在GUI应用程序中,当用户与界面进行交互(例如点击按钮或拖动滑块)时,界面的某些部分可能需要更新以反映这些变化。观察者模式允许这些部分(观察者)自动响应这些事件,而无需手动调用更新方法。
  • 游戏开发:在游戏中,玩家和NPC的行为,如移动、攻击、受伤等,都可能触发一系列事件。观察者模式可以用来注册和通知这些事件的监听器,以便游戏逻辑能够实时响应并更新游戏状态。
  • 消息传递系统:在分布式系统或微服务架构中,组件之间需要传递消息以实现通信和协作。观察者模式允许一个组件(被观察者)在产生消息时通知所有订阅了该消息的组件(观察者),从而实现高效的消息传递。
  • 实时数据监控:在需要实时跟踪和响应数据变化的场景中,如股票价格监控、传感器数据读取等,观察者模式可以帮助实现当数据变化时自动触发相应的处理逻辑。
  • 状态管理:当应用程序中某个对象的状态发生变化,并且需要通知其他对象进行相应的处理时,可以使用观察者模式。例如,一个订单状态的改变可能需要通知库存系统、支付系统等多个系统进行相应的操作。

案例1

推方式实现,以广播方式发送消息所有观察者

UML

在这里插入图片描述

实现步骤:

  • 创建观察者接口,定义通过被观察者更新状态接口
  • 创建被观察者,被观察者持有所有观察者的引用,提供注册、移除、通知所有观察者的方法,定义状态属性,提供get/set方法set方法中默认通知所有观察者

实现代码

Observer.java

// 观察者接口
public interface Observer {
    void update(Subject subject);
}

Subject.java

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

// 被观察者
public class Subject {
    // 定义存储观察者的集合
    private List<Observer> observerList = new ArrayList<Observer>();
    // 定义状态属性
    private String state;

    // 注册观察者
    public void registerObserver(Observer observer){
        observerList.add(observer);
    }

    // 移除观察者
    public void removeObserver(Observer observer){
        observerList.remove(observer);
    }

    // 通知所有观察者
    public void notifyObservers(){
        for(Observer observer : observerList){
            observer.update(this);
        }
    }

    public String getState() {
        return state;
    }

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

ObserverA.java

// 观察者A
public class ObserverA implements Observer{
    @Override
    public void update(Subject subject) {
        System.out.printf("观察者A接收到消息-state:%s%n",subject.getState());
    }
}

ObserverB.java

// 观察者B
public class ObserverB implements Observer{
    @Override
    public void update(Subject subject) {
        System.out.printf("观察者B接收到消息-state:%s%n",subject.getState());
    }
}

TestClient.java

public class TestClient {
    public static void main(String[] args) {
        Subject subject = new Subject();
        subject.registerObserver(new ObserverA());
        subject.registerObserver(new ObserverB());
        subject.setState("START");
    }
}

执行结果:

在这里插入图片描述

案例2

使用java.util提供的Observable,Observer类实现案例1。

该方式已在1.9版本中移除,不再推荐使用

实现代码

ConcreteSubject.java

import java.util.Observable;

// 目标对象
public class ConcreteSubject extends Observable {

    private String state;

    public void setState(String state) {
        this.state = state;
        // 表示目标对象已被改变
        setChanged();
        // 通知所有观察者
        notifyObservers();
    }

    public String getState() {
        return state;
    }
}

ObserverA.java

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

/**
 * 观察者A
 *
 * @author Anna.
 * @date 2024/4/25 16:15
 */
public class ObserverA implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.printf("观察者A接收到消息-state:%s%n", ((ConcreteSubject) o).getState());
    }
}

ObserverB.java

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

// 观察者B
public class ObserverB implements Observer {
    @Override
    public void update(Observable o, Object arg) {
        System.out.printf("观察者B接收到消息-state:%s%n", ((ConcreteSubject) o).getState());
    }
}

TestClient.java

public class TestClient {
    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        subject.addObserver(new ObserverA());
        subject.addObserver(new ObserverB());

        subject.setState("STOP");
    }
}

执行结果:
在这里插入图片描述

案例3

通过拉方式,实现观察者主动拉取被观察者当前状态

UML

在这里插入图片描述

实现步骤:

  • 创建被观察者Subject,定义状态属性并提供get/set方法
  • 定义观察者接口类,提供拉取状态接口
  • 创建具体的观察者,实现观察者接口,通过持有被观察者的引用,主动拉取被观察者当前状态

实现代码

Subject.java

// 被观察者
public class Subject {
    // 定义当前状态
    private String state;
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
}

Observer.java

// 观察者接口
public interface Observer {
    // 定义拉取信息的接口
    void update();
}

ConcreteObserver.java

// 观察者具体实现
public class ConcreteObserver implements Observer {
    // 定义被观察者,持有被观察者的引用
    private Subject subject;
    public ConcreteObserver(Subject subject) {
        this.subject = subject;
    }
    @Override
    public void update() {
        System.out.printf("观察者主动拉取状态-state:%s%n ", subject.getState());
    }
}

TestClient.java

public class TestClient {
    public static void main(String[] args) {
        // 创建被观察者
        Subject subject = new Subject();
        // 创建观察者
        ConcreteObserver observer = new ConcreteObserver(subject);
        // 修改状态
        subject.setState("RUN");
        // 观察者主动拉取
        observer.update();
    }
}

执行结果:

在这里插入图片描述

gitee源码

git clone https://gitee.com/dchh/JavaStudyWorkSpaces.git

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

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

相关文章

Python 基础 (Pandas):Pandas 入门

1. 官方文档 API reference — pandas 2.2.2 documentation 2. 准备知识&#xff1a;Pandas 数据结构 Series & DataFrame 2.1 Series 2.1.1 创建 Series 类型数据 一个 Series 对象包含两部分&#xff1a;值序列、标识符序列。可通过 .values (返回 NumPy ndarry 类型…

Swift - 基础语法

文章目录 Swift - 基础语法1. 常量1.1 只能赋值1次1.2 它的值不要求在编译时期确定&#xff0c;但使用之前必须赋值1次1.3 常量、变量在初始化之前&#xff0c;都不能使用 2. 标识符3. 常用数据类型4. 字面量4.1 布尔4.2 字符串4.3 整数4.4 浮点数4.5 数组4.6 字典 5. 类型转换…

【C++】string类的增删改查模拟实现(图例超详细解析!!!)

目录 一、前言 二、string类的模拟实现 ✨前情提要 ✨Member functions —— 成员函数 ⚡构造函数 ⚡拷贝构造函数 ⚡赋值运算符重载 ⚡析构函数 ✨Element access —— 元素访问 ⚡operator[ ] ⚡Iterator —— 迭代器 ✨Capacity —— 容量 ⚡size ⚡capacity ⚡clea…

记录一个Maxwell采集MySQL数据时报安全证书时间不通过的问题

【背景描述】 我的zk&#xff0c;kafka和Maxwell都正常启动了 此时我需要用Maxwell将MySQL的一张表user_info将其全量同步到kafka当中时发生报错&#xff0c;命令如下&#xff1a; [atguiguhadoop102 datas]$ /opt/module/maxwell/bin/maxwell-bootstrap --database gmall --…

Mongodb语法使用说明(含详细示例)

点击下载《Mongodb语法使用说明&#xff08;含详细示例&#xff09;》 1. 前言 MongoDB是一款高性能、开源、面向文档的NoSQL数据库&#xff0c;它使用类似JSON的BSON格式存储数据&#xff0c;提供了灵活的数据模型和强大的查询功能。本文将详细介绍MongoDB数据库的基本增删改…

机器学习-期末复习

本文的内容按照作者的课程考试要求书写&#xff0c;仅供复习参考。&#x1f337;&#x1f337;&#x1f337;欢迎大家指正&#xff01; 机器学习是一种人工智能&#xff08;AI&#xff09;的分支领域&#xff0c;它致力于开发能够通过数据学习和改进的算法和模型。简而言之&…

深入学习Linux中的“文件系统与日志分析”

目录 1.文件系统的组成 1.1inode和block 1.2inode的内容 1.3inode的号码 ​1.4文件存储小结 ​1.5inode大小 1.6inode的特殊作用 2. 链接文件 3.文件恢复 3.1EXT类型文件恢复 3.2磁盘有空间&#xff0c;但是仍然无法写入新文件 3.3恢复XFS类型的文件 3.3.1xfsdump…

面试:JVM内存结构

一、Java代码的运行步骤 一段Java代码先会被反编译为Java字节码&#xff0c;当执行java命令时&#xff0c;JVM虚拟机会被创建出来&#xff0c;并会创建一个main主线程来执行主方法。 二、JVM的内存结构有哪些&#xff1f; 1、方法区&#xff1a;&#xff08;线程共享&#xff…

实验五 Spark SQL编程初级实践

Spark SQL编程初级实践 Spark SQL基本操作 将下列JSON格式数据复制到Linux系统中&#xff0c;并保存命名为employee.json。 { "id":1 , "name":" Ella" , "age":36 } { "id":2, "name":"Bob","a…

婴儿专用洗衣机有必要吗?四大宝藏婴儿洗衣机测评对比

对于有了宝宝的家庭来说&#xff0c;洗衣成为了一项重要的家务事。大家都知道&#xff0c;宝宝的皮肤比较娇嫩&#xff0c;容易受到各种细菌、病毒的侵扰。所以&#xff0c;宝宝的衣物应该与大人的分开洗。婴儿洗衣机作为一种专门为婴幼儿家庭设计的洗衣机&#xff0c;其具有除…

Ubuntu 20.04.6下载、安装

一、下载 下载地址&#xff1a;https://cn.ubuntu.com/download 下载版本&#xff1a;ubuntu-20.04.6-desktop-amd64.iso 二、安装 参考博客&#xff1a; https://blog.csdn.net/lhl_blog/article/details/123406322 https://www.cnblogs.com/fieldtianye/p/17879840.html…

根据当年节假日和非工作时间计算请假时间-获取每个月的节假日,计算每个月的工作日时间进度,节假日每年更新

根据需求请假时间要排除法定节假日和非工作时间 1.获取当年的节假日 节假日是每年更新的&#xff0c;没有固定接口&#xff0c;需要手动录入 个人根据官方的节假日整理了当年的所有节假日&#xff0c;可以根据个人需求进行修改 // 获取每个月的节假日&#xff0c;如果当月没…

【Qt 学习笔记】Qt常用控件 | 显示类控件 | LCD Number的使用及说明

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Qt 专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Qt常用控件 | 显示类控件 | LCD Number的使用及说明 文章编号&#xf…

ELK技术介绍:背景、功能及应用场景全面解析

一、ELK概述 ELK是由Elasticsearch、Logstash和Kibana三个开源软件组成的日志管理解决方案&#xff0c;这一组合在近年来得到了广泛的关注和应用。ELK的出现&#xff0c;源于大数据和云计算技术的快速发展&#xff0c;以及对高效日志管理的迫切需求。 随着企业信息化程度…

3dmax云渲染100插件怎么安装?渲染100邀请码1a12

3dmax云渲染插件能在设计师完成参数设置后&#xff0c; 通过点击插件的方式上传到云渲染平台进行渲染&#xff0c;那么3dmax云渲染插件怎么安装呢&#xff1f;以渲染100为例&#xff0c;我们来看下。 下载工具&#xff1a;渲染100客户端 1、设计师在渲染100官网(http://www.x…

淘宝扭蛋机小程序开发:开启购物新纪元,探索乐趣无穷的互动体验

随着科技的飞速发展&#xff0c;人们的购物方式也在不断革新。淘宝扭蛋机小程序应运而生&#xff0c;它巧妙地结合了线上购物与线下娱乐&#xff0c;为消费者带来了一种前所未有的互动体验。今天&#xff0c;就让我们一起走进淘宝扭蛋机小程序的开发世界&#xff0c;探索其背后…

DHCP原理和配置

1、DHCP原理 &#xff08;1&#xff09;什么是DHCP DHCP(Dynamic HostConfiguration Protocol,动态主机配置协议)&#xff1a;给网络内的客户机自动分配IP地址由internet工作任务小组设计开发口专门用于为TCP/IP网络中的计算机自动分配TCP/IP参数的协议DHCP采用的是UDP作为传输…

【数字图像处理笔记】Matlab实现离散傅立叶变换 (二)

&#x1f48c; 所属专栏&#xff1a;【数字图像处理笔记】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &#x…

离线语音模块初步学习——LSYT201B(深圳雷龙发展)

一 、产品简介 首先简单介绍下该离线语音模块&#xff0c;官方给出的介绍是&#xff1a;YT2228 是根据智能语音交互市场需求及思必驰算法的发展方向定义开发的 “芯片算法”人工智能人机语音交互解决方案&#xff0c;具有高性能、低功耗等特点。该芯片通过软硬融合的方法&…

流量代理第一弹:入门使用

定义 “流量代理是一种网络通信技术&#xff0c;它充当网络通信的中间人&#xff0c;将流量从一个地方传递到另一个地方。通常用于实现网络隧道、网络加速、访问控制和隐私保护等功能。“ 上面是来自chatGPT的回答。从这个回答中&#xff0c;我们不难看出&#xff0c;流量代理…