Java 观察者模式 详解

news2024/10/7 16:16:06

观察者模式是一种常见的设计模式,也称作发布-订阅模式。它主要解决了对象之间的通知依赖关系问题。在这种模式中,一个对象(称作Subject)维护着一个对象列表,这些对象(称作Observers)都需要被通知来响应某些事件。

观察者模式的好处在于它创建了松耦合的对象之间的一对多关系,使得Subject和Observers之间的耦合度降低。在Java中,通过定义接口和抽象类等方式,可以很容易实现观察者模式。

  1. 观察者模式概述

观察者模式包含两个主要角色:Subject和Observer。Subject是需要被监视的对象,Observer则是负责观察Subject的变化记录并做出相应处理的对象。

当Subject发生变化时,会通知所有的Observer,并调用其相应的回调方法(也就是Observer中定义的处理Subject变化的方法)。因此,每个Observer都需要注册到Subject中去,以便Subject在发生变化时能够通知到它们。

观察者模式总体结构如下所示:
在这里插入图片描述

其中,Subject包含一个状态(state)和一个Observer列表(observers),它有如下几个主要方法:

  • registerObserver(Observer observer):将一个Observer对象注册到Subject中。

  • removeObserver(Observer observer):将一个Observer对象从Subject中移除。

  • notifyObservers():通知所有已经注册的Observer,以便它们做出相应的处理。

Observer接口则主要定义Observer对象中需要实现的处理Subject变化的方法。在Java中,通常将Observer接口定义为如下所示:

public interface Observer {
    public void update(Object arg);
}

其中,update方法用于处理Subject发生变化时的操作。

  1. Java中观察者模式的实现

在Java中,实现观察者模式有多种方式。下面介绍两种比较常见的实现方式:使用Java内置类库提供的Observer和Observable类实现和使用自定义接口实现。

2.1 使用Java内置类库提供的Observer和Observable类实现

Java内置类库提供了一个Observer接口和一个Observable类,可以方便地实现观察者模式。

Observable类实际上就是Subject,它有如下方法:

  • addObserver(Observer o):注册一个Observer对象到Observable中。

  • deleteObserver(Observer o):从Observable中删除指定的Observer对象。

  • notifyObservers():通知所有已经注册的Observer,以便它们做出相应的处理。

Observer接口同上。

下面是一个使用这种方式实现观察者模式的简单示例:

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

public class SimpleObservable extends Observable {
    private int data = 0;

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
        setChanged(); // 标记Subject对象的状态已发生变化
        notifyObservers(data); // 通知所有已注册的Observer对象
    }
}

public class SimpleObserver implements Observer {
    private int data;

    public void update(Observable o, Object arg) {
        if (arg instanceof Integer) {
            this.data = (Integer) arg;
            System.out.println("Data has changed to " + this.data + " in SimpleObserver.");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        SimpleObservable observable = new SimpleObservable();
        SimpleObserver observer = new SimpleObserver();

        observable.addObserver(observer);
        observable.setData(1);
        observable.setData(2);
        observable.setData(3);
    }
}

这个示例中,SimpleObservable类继承了Observable,并在setData方法中使用了setChanged和notifyObservers方法,标记Subject对象的状态已发生变化并通知所有已注册的Observer对象。

SimpleObserver类实现了Observer接口,并在update方法中处理Subject发生变化时的操作。

最后,Test类将SimpleObservable和SimpleObserver组装起来,实现了一个简单的观察者模式示例。

需要注意的是,Java内置的Observable类有几个限制:

  • Observable是一个类而不是一个接口,这意味着你不能再继承其他类。

  • setChanged方法被定义为protected,因此你不能显式地调用这个方法。

  • Observer接口中只有一个update方法,而Observable没有额外的状态变量来描述它的内部状态,这使得Observer无法获得有关Observable的状态信息。

  • 如果Observable涉及到线程安全问题,你需要在使用Observable时自己处理。

2.2 使用自定义接口实现

另一种实现观察者模式的方式是使用自定义接口,这种方式比较灵活,也可以适用于一些特定场景。

下面是一个使用这种方式实现观察者模式的简单示例:

public interface Observer {
    public void update(Object obj);
}

public interface Subject {
    public void registerObserver(Observer observer);
    public void removeObserver(Observer observer);
    public void notifyObservers(Object obj);
}

public class SimpleSubject implements Subject {
    private List<Observer> observers = new ArrayList<Observer>();
    private int data = 0;

    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    public void notifyObservers(Object obj) {
        for (Observer observer : observers) {
            observer.update(obj);
        }
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
        notifyObservers(data);
    }
}

public class SimpleObserver implements Observer {
    private int data;

    public void update(Object obj) {
        if (obj instanceof Integer) {
            this.data = (Integer) obj;
            System.out.println("Data has changed to " + this.data + " in SimpleObserver.");
        }
    }
}

public class Test {
    public static void main(String[] args) {
        SimpleSubject subject = new SimpleSubject();
        SimpleObserver observer = new SimpleObserver();

        subject.registerObserver(observer);
        subject.setData(1);
        subject.setData(2);
        subject.setData(3);
    }
}

这个示例中,Subject接口定义了三个方法:registerObserver、removeObserver和notifyObservers。SimpleSubject类实现了Subject接口,并在setData方法中调用了notifyObservers方法。

Observer接口定义了update方法,SimpleObserver类实现了Observer接口,并在update方法中处理Subject发生变化时的操作。

最后,Test类将SimpleSubject和SimpleObserver组装起来,实现了一个简单的观察者模式示例。

自定义接口的优点在于它的灵活性,能够适用于各种场景。另外,自己实现Observer模式通常需要写更多的代码,并且Observer对象可能需要自己维护许多状态信息。

  1. 观察者模式的优点和缺点

观察者模式具有以下优点:

  • 观察者模式将Subject和Observer之间解耦,使得它们可以独立地变化或扩展,提高了系统的灵活性和可扩展性。

  • 观察者模式建立了一种灵活的一对多关系,可以在任何时候添加或删除Observer对象,实现不同的业务需求。

  • 当Subject发生变化时,只需要通知已经注册的Observer对象,而无需通知未注册的Observer对象,从而避免了系统产生不必要的开销。

观察者模式具有以下缺点:

  • 观察者模式可能会导致系统中出现大量的细粒度对象,从而增加系统的复杂性。

  • 如果Subject和Observer之间相互依赖,会导致循环依赖问题,从而导致系统崩溃。

总之,观察者模式是一种常用的设计模式,主要解决了对象之间的通知依赖关系问题。在Java中,可以使用Java内置类库提供的Observer和Observable类或者自定义接口实现观察者模式。观察者模式具有灵活性、可扩展性和提高系统松耦合度等优点,但也会带来一些复杂性和循环依赖问题。在使用观察者模式时,需要根据具体业务场景进行权衡和选择。

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

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

相关文章

STANet代码复现出现的问题

1 IndexError: boolean index did not match indexed array along dimension 0; dimension is 4194304 but corresponding boolean dimension is 65536定位到导致错误的代码&#xff0c;是metric.py&#xff0c;Collect values for Confusion Matrix 收集混淆矩阵的值时出错 …

Java框架学习02(SpringSpringBoot常用注解总结)

1. SpringBootApplication 这里先单独拎出SpringBootApplication 注解说一下&#xff0c;虽然我们一般不会主动去使用它。 Guide&#xff1a;这个注解是 Spring Boot 项目的基石&#xff0c;创建 SpringBoot 项目之后会默认在主类加上。 SpringBootApplication public class…

ROM、RAM、FLASH区别

文章目录 一、ROM二、RAM三、FLASH四、单片机程序大小计算1、Keil/MDK 一、ROM ROM&#xff08;Read Only Memory&#xff09;只读存储器。是一种半导体内存&#xff0c;其特性是一旦储存资料就无法再将之改变或删除。通常用在不需经常变更资料的电子或电脑系统中&#xff0c;资…

性能测试的核心原理

性能测试的核心原理 1 基于协议&#xff0c;前后端交互机制&#xff0c;性能核心。基于界面决定和前端用户交互&#xff0c;基于代码决定了后端。 1 网络分布式架构。 2 单机应用&#xff0c;比如安安兔&#xff0c;鲁大师。主要判断io读写&#xff0c;以及对资源的消耗。 2 多…

git常见操作命令

1.Git 配置 在安装完成 Git 后&#xff0c;开始正式使用前&#xff0c;是需要有一些全局设置的&#xff0c;如用户名、邮箱。 git config --global user.name "your name" // 设置全局用户名 git config --global user.email "your email" // 设…

Linux诞生与分支

a) 什么是操作系统操作系统是计算机系统中必不可少的基础系统软件&#xff0c;它的作用是管理和控制计算机系统中的硬件和软件资源&#xff0c;合理地组织计算机系统的工作流程&#xff0c;以便有效地利用这些资源为使用者提供一个功能强大、使用方便的操作环境。它在计算机系…

汽车轮胎充电宝打气泵方案

我们知道新能源车是没有配置充气泵的&#xff0c;所以在平时日常使用中我们还需要配置一个充气泵。充气泵方案便是在这个用户需求上面开发出来的。它体积小、外观精美、带有多模式充气并车胎检测等功能&#xff0c;是现在有车一族的出行必备物品。 充气泵方案其功能设计集成于一…

【语义分割】标注工具ISAT with segment anything介绍

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 极速分割标注工具 1. 正文 1.1 安装 创建虚拟环境 conda create -n ISAT_with_segment_anything python3.8 conda activate ISAT_with_segment_anyt…

宝塔面板搭建Discuz论坛并发布互联网访问【无需云服务器】

✨个人主页&#xff1a;bit me&#x1f447; 目 录 ⏳前言⏰1.安装基础环境⌚️2.一键部署Discuz&#x1f4fb;3.安装cpolar工具&#x1f4e1;4.配置域名访问Discuz&#x1f50d;5.固定域名公网地址&#x1f4f2;6.配置Discuz论坛 转载自cpolar极点云的文章&#xff1a;Linux宝…

关于startActivityForResult()方法被弃用的解决方案

关于startActivityForResult()方法被弃用的解决方案 背景&#xff1a;使用startActivityForResult()方法时&#xff0c;代码出现划线&#xff0c;显示该方法已被弃用 startActivityForResult() 方法是一种用于在 Android 应用程序中启动另一个活动&#xff08;Activity&#x…

Charles抓包

mac 1、下载安装包 官网下载&#xff1a;Download a Free Trial of Charles • Charles Web Debugging Proxy 2、有效期&#xff1a; 打开Charles > Help > Register Charles > 输入 Registered Name &#xff1a; https://zhile.io License Key&#xff1a;48891…

“正大杯”第十三届市场调查与分析大赛[省一]经验总结+复盘

目录 1 前期组队 2 队员组成 队长-成员1 应用统计学专业 成员2 化学实验专业 成员3-本人 物联网工程专业 成员4 金融ACCA专业 成员5 应用物理学 总结 3 比赛进度 3月中旬 部分图表的制作 问卷设计与制作 稍微改动主题 问卷相关总结 前期调查部分论文框架 3月…

手拉手教您Linux搭建web

文章目录 前言1. 本地搭建web站点2. 测试局域网访问3. 公开本地web网站3.1 安装cpolar内网穿透3.2 创建http隧道&#xff0c;指向本地80端口3.3 配置后台服务 4. 配置固定二级子域名5. 测试使用固定二级子域名访问本地web站点 转载自cpolar文章&#xff1a;Linux CentOS本地搭建…

【2023 · CANN训练营第一季】应用开发深入讲解——第四章 DVPP初级

学习资源 1.JPEG图片解码 文档 2.图片缩放 文档 媒体数据处理 数据预处理的典型使用场景 数据预处理的多种方式 分为两种&#xff0c;AIPP和DVPP DVPP数据预处理功能 了解两个重要概念 宽stride 和 高stride 理解&#xff1a;这里假设一张图片的宽为500&#xff0c;高为3…

SpringBoot Properties YML文件的优先级

SpringBoot启动文件的优先顺序测试 运行时指定加载文件时&#xff0c;最高Java jar spring.config.location优先于spring.profiles.activeJava -jar 三种语法 运行时不指定加载文件时&#xff0c;其次1.项目路径下的config目录优先级最高---12.项目路径的优先级其次---23.Eclip…

linux:查找相关命令find与grep

find 语法 在指定目录下查找文件和目录。 find [path] [expression]path&#xff1a;是要查找的目录路径&#xff0c;可以是一个目录或文件名&#xff0c;也可以是多个路径&#xff0c;多个路径之间用空格分隔&#xff0c;如果未指定路径&#xff0c;则默认为当前目录。 exp…

Kotlin高级协程

Kotlin高级协程 一.前言二.先从线程说起三.协程的设计思想四.协程特点&#xff1a;优雅的实现移步任务五.协程基本使用六.协程和线程相比有什么特点&#xff0c;如何优雅的实现异步任务 一.前言 在文章正式上干货之前&#xff0c;先说一点背景吧&#xff1b;我是 Kotlin 协程官…

C语言初阶之扫雷代码详解(含递归展开)

扫雷代码详解 扫雷代码思路头文件解析主函数解析函数文件解析①初始化函数&#xff08;InitBoard&#xff09;②打印函数③难易选项函数&#xff08;Select&#xff09;④布雷&#xff08;SetMine&#xff09;⑤获取周边雷数&#xff08;GetMine&#xff09;⑥展开函数&#xf…

从零开始,轻松打造Web自动化测试框架:Python+Selenium实战指南

在程序员的世界中&#xff0c;一切重复性的工作&#xff0c;都应该通过程序自动执行。「自动化测试」就是一个最好的例子。 随着互联网应用开发周期越来越短&#xff0c;迭代速度越来越快&#xff0c;只会点点点&#xff0c;不懂开发的手工测试&#xff0c;已经无法满足如今的…

广州虚拟动力亮相2023世界元宇宙生态博览会,邀您解锁元宇宙商机

2023年5月10-12日 2023第2届世界元宇宙生态博览会 在广州广交会展馆正式举办&#xff01; 广州虚拟动力携虚拟数字人全栈式解决方案参展 诚邀您莅临3.2馆A109-A110展位参观交流 2022年全国多个地方出台元宇宙政策文件&#xff0c;各地方政府的元宇宙产业扶持政策也在逐渐细…