观察者(订阅)模式

news2024/7/7 17:17:41

文章目录

    • 思考观察者模式
      • 1.观察者模式的本质
      • 2.何时选用观察者模式
      • 3.优缺点
      • 4.实现
        • 手写观察者模式
        • JDK观察者模式

思考观察者模式

观察者模式是典型的发布订阅模式,当一个东西有变化了,就通知所有订阅他的人

1.观察者模式的本质

观察者模式的本质:触发联动。

当修改目标对象的状态的时候,就会触发相应的通知,然后会循环调用所有注册的观察者对象的相应方法,其实就相当于联动调用这些观察者的方法。

而且这个联动还是动态的,可以通过注册和取消注册来控制观察者,因而可以在程序运行期间,通过动态地控制观察者,来变相地实现添加和删除某些功能处理,这些功能就是观察者在update的时候执行的功能。

同时目标对象和观察者对象的解耦,又保证了无论观察者发生怎样的变化,目标对象总是能够正确地联动过来。

理解这个本质对我们非常有用,对于我们识别和使用观察者模式有非常重要的意义,尤其是在变形使用的时候。万变不离其宗。

2.何时选用观察者模式

建议在以下情况中选用观察者模式。

  • 当一个抽象模型有两个方面,其中一个方面的操作依赖于另一个方面的状态变化,那么就可以选用观察者模式,将这两者封装成观察者和目标对象,当目标对象变化的时候,依赖于它的观察者对象也会发生相应的变化。这样就把抽象模型的这两个方面分离开了,使得它们可以独立地改变和复用。

  • 如果在更改一个对象的时候,需要同时连带改变其他的对象,而且不知道究竟应该有多少对象需要被连带改变,这种情况可以选用观察者模式,被更改的那一个对象很明显就相当于是目标对象,而需要连带修改的多个其他对象,就作为多个观察者对象了。

  • 当一个对象必须通知其他的对象,但是你又希望这个对象和其他被它通知的对象是松散耦合的。也就是说这个对象其实不想知道具体被通知的对象。这种情况可以选用观察者模式,这个对象就相当于是目标对象,而被它通知的对象就是观察者对象了。

3.优缺点

观察者模式具有以下优点

  • 观察者模式实现了观察者和目标之间的抽象耦合
    原本目标对象在状态发生改变的时候,需要直接调用所有的观察者对象,但是抽象出观察者接口以后,目标和观察者就只是在抽象层面上耦合了,也就是说目标只是知道观察者接口,并不知道具体的观察者的类,从而实现目标类和具体的观察者类之间解耦。

  • 观察者模式实现了动态联动
    所谓联动,就是做一个操作会引起其他相关的操作。由于观察者模式对观察者注册实行管理,那就可以在运行期间,通过动态地控制注册的观察者,来控制某个动作的联动范围,从而实现动态联动。

  • 观察者模式支持广播通信
    由于目标发送通知给观察者是面向所有注册的观察者,所以每次目标通知的信息就要对所有注册的观察者进行广播。当然,也可以通过在目标上添加新的功能来限制广播的范围。
    在广播通信的时候要注意一个问题,就是相互广播造成死循环的问题。比如A和B两个对象互为观察者和目标对象,A对象发生状态变化,然后A来广播信息,B对象接收到通知后,在处理过程中,使得B对象的状态也发生了改变,然后B来广播信息,然后A对象接到通知后,又触发广播信息……,如此A引起B变化,B又引起A变化,从而一直相互广播信息,就造成死循环。

观察者模式的缺点是:

  • 可能会引起无谓的操作
    由于观察者模式每次都是广播通信,不管观察者需不需要,每个观察者都会被调用update方法,如果观察者不需要执行相应处理,那么这次操作就浪费了。其实浪费了还好,最怕引起误更新,那就麻烦了,比如,本应该在执行这次状态更新前把某个观察者删除掉,这样通知的时候就没有这个观察者了,但是现在忘掉了,那么就会引起误操作。

4.实现

在这里插入图片描述

模拟:手机降价通知用户

手写观察者模式

1.目标类

/**
 * @description:通用目标接口,也可为抽象类
 */
public class Subject {

    /**
     * 维护一个观察者列表
     */
    private List<Observer> list=new ArrayList<>();

    /**
     * 注册观察者
     * @param observer
     */
    public void attach(Observer observer){
        list.add(observer);
    }

    /**
     * 移除观察者
     * @param observer
     */
    public void detach(Observer observer){
        list.remove(observer);
    }

    /**
     * 通知观察者
     */
    public void notifyObservers(){
        list.stream().forEach(obj->obj.update(this));
    }
}

/**
 * @description:手机目标对象
 */
@Getter
@ToString
public class PhoneSubject extends Subject{

    /**
     * 手机价格
     */
    private double price;

    public void setPrice(double price) {
        this.price = price;
        //价格小于150为降价
        if (price<150){
            //通知用户降价
            notifyObservers();
        }
    }
}

2.观察者类

/**
 * @description:观察者接口
 */
public interface Observer {

    /**
     * 被通知的方法
     * @param subject 目标对象
     */
    void update(Subject subject);
}

/**
 * @description:用户(观察者)
 */
@Data
public class UserObserver implements Observer{

    /**
     * 用户名
     */
    private String name;

    @Override
    public void update(Subject subject) {
        System.out.println(this.name +"收到了降价通知,价格为"+((PhoneSubject)subject).getPrice());
    }
}

3.测试类

/**
 * @description:测试类
 */
public class Client {

    public static void main(String[] args) {
        UserObserver userObserver1 = new UserObserver();
        userObserver1.setName("张三");

        UserObserver userObserver2 = new UserObserver();
        userObserver2.setName("李四");

        PhoneSubject subject=new PhoneSubject();
        //初始价格为150
        subject.setPrice(150);
        //绑定观察者
        subject.attach(userObserver1);
        subject.attach(userObserver2);

        //涨价不通知
        subject.setPrice(250);

        //降价才通知用户,第一次降价
        subject.setPrice(100);

        //降价才通知用户,第二次降价
        subject.setPrice(88);
    }
}

4.效果
在这里插入图片描述

JDK观察者模式

1.目标类继承Observable接口即可

/**
 * @description:手机目标对象
 */
@Getter
@ToString
public class PhoneSubject2 extends Observable {

    /**
     * 手机价格
     */
    private double price;

    public void setPrice(double price) {
        this.price = price;
        //价格小于150为降价
        if (price<150){
            //通知用户降价
            notifyObservers();
        }
    }
}

里面维护了观察者对象,以及注册、移除观察者,通知观察者等
在这里插入图片描述
2.观察者类实现Observer接口即可

/**
 * @description:用户1
 */
@Data
public class UserObserver2 implements Observer {

    /**
     * 用户名
     */
    private String name;
    
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof PhoneSubject2){
            System.out.println(this.name +"收到了降价通知,价格为"+((PhoneSubject2)o).getPrice());
        }
    }
}

和手写的一模一样
在这里插入图片描述

3.测试类

/**
 * @author conggq
 * @description:测试类
 * @createTime 2022/11/29 17:27
 */
public class Client {

    public static void main(String[] args) {
        UserObserver userObserver1 = new UserObserver();
        userObserver1.setName("张三");

        UserObserver userObserver2 = new UserObserver();
        userObserver2.setName("李四");

        PhoneSubject2 subject2=new PhoneSubject2();
        //初始价格为150
        subject2.setPrice(150);

        //涨价不通知
        subject.setPrice(250);

        //降价才通知用户,第一次降价
        subject.setPrice(100);

        //降价才通知用户,第二次降价
        subject.setPrice(88);
    }
}

结果一样

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

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

相关文章

什么是等保

等保的全称是信息安全等级保护&#xff0c;是《网络安全法》规定的必须强制执行的&#xff0c;保障公民、社会、国家利益的重要工作。以下是一些有关等保的基本知识&#xff0c;希望通过这些知识能让大家更深刻地认识到等级保护的重要性。 等级保护定义 信息安全等级保护是指…

2.Conv2d实现

[C 基于Eigen库实现CRN前向推理] 第二部分&#xff1a;Conv2d实现 前言&#xff1a;(Eigen库使用记录)第一部分&#xff1a;WavFile.class (实现读取wav/pcm,实现STFT)第二部分&#xff1a;Conv2d实现第三部分&#xff1a;TransposedConv2d实现 (mimo,padding,stride,dilatio…

智工教育:注册计量师职业资格条件已改革!

第一&#xff0c;报名条件发生变化 注册计量师职业资格考试&#xff0c;取消了一级注册计量师职业资格考试报考条件中对工作年限的要求。 凡遵守中华人民共和国宪法、法律、法规&#xff0c;恪守职业道德&#xff0c;诚实守信&#xff0c;从事计量技术工作&#xff0c;符合注册…

Codeforces Round #290 (Div. 2) C. Fox And Names

翻译&#xff1a; Fox Ciel将发表一篇关于FOCS (Fox操作的计算机系统&#xff0c;发音:“Fox”)的论文。她听到一个谣言:报纸上的作者名单总是按照词典顺序排列的。 在查看了一些例子后&#xff0c;她发现有时这不是真的。在一些论文中&#xff0c;作者的名字没有按照正常意义…

【无人机】四轴无人机的轨迹进行可视化和动画处理(Matlab代码实现)

&#x1f4cb;&#x1f4cb;&#x1f4cb;本文目录如下&#xff1a;⛳️⛳️⛳️ ​ 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 随着传感器检测技术、智能控制技术和材料技术的快速发展,四轴无人机及其配套系统的发展越来越成熟。无人机遥感系统具有成本低、…

Java数据结构之Map与Set

文章目录一、搜索&#xff08;一&#xff09;概念及场景&#xff08;二&#xff09;模型二、Map&#xff08;一&#xff09;介绍&#xff08;二&#xff09;Map常用方法说明1.需要注意的几个点2.特别注意的几个方法(1)V getOrdefault(Object key,V defaultValue)&#xff0c;这…

【DL with Pytorch】第 5 章 :风格迁移

&#x1f50e;大家好&#xff0c;我是Sonhhxg_柒&#xff0c;希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流&#x1f50e; &#x1f4dd;个人主页&#xff0d;Sonhhxg_柒的博客_CSDN博客 &#x1f4c3; &#x1f381;欢迎各位→点赞…

[JavaEE]计算机是如何工作的

专栏简介: JavaEE从入门到进阶 题目来源: leetcode,牛客,剑指offer. 创作目标: 记录学习JavaEE学习历程 希望在提升自己的同时,帮助他人,,与大家一起共同进步,互相成长. 学历代表过去,能力代表现在,学习能力代表未来! 目录 1. javaEE概述 2. 计算机发展史 3. 冯诺依曼体系 …

java面试强基(15)

说明一下public static void main(String args[])这段声明里每个关键字的作用? ​ public: main方法是Java程序运行时调用的第一个方法&#xff0c;因此它必须对Java环境可见。所以可见性设置为 pulic. ​ static: Java平台调用这个方法时不会创建这个类的一个实例&#xf…

如何将带GPS的网络化的软件定义无线电接收机应用于分布式和移动频谱监测?(二)

GPS定位和测向的四种技术 知道感兴趣信号的位置对于许多应用很重要。军事用户获得了更好的态势感知能力&#xff0c;诸如机场或公用事业基础设施之类的敏感设施可以找到RF干扰源&#xff0c;电信公司可以识别恶意发射机或其他干扰其覆盖范围的设备。通过嵌入式GPS功能了解测量…

中国什么时候能办世界杯?

自从1930年在乌拉圭举办了第一届世界杯以来&#xff0c;到现在已经成功举办了22届&#xff0c;然而这22届里&#xff0c;光是欧洲就举办了11届&#xff0c;南美洲5届&#xff0c;中北美洲3届&#xff0c;亚洲2届&#xff0c;非洲1届。 说到这里不难发现&#xff0c;他们之间分布…

Azure DevOps Server 用户组加入 Azure AD Domain Service 管理用户

一&#xff0c;引言 今天我们继续讲解 Azure DevOps Server 的内容&#xff0c;对于管理用户组除了在 Azure DevOps Server 服务器上添加管理员方式外&#xff0c;还有没有其他方式&#xff0c;Azure DevOps 需要加入Azure ADDS 服务域后&#xff0c;Azure DevOps Server 的管理…

[附源码]计算机毕业设计springboot基于vue+mysql开发的考试系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

图扑软件荣获第七届“创客中国”中小企业创新创业大赛优胜奖!

2022 年 11 月 17 日&#xff0c;由工业和信息化部、财政部共同主办的第七届“创客中国”中小企业创新创业大赛全国总决赛在浙江杭州落下帷幕。 本次《第七届“创客中国”中小企业创新创业大赛》举办目的&#xff0c;意在加大优质中小企业梯度培育力度&#xff0c;进一步提升中…

ProcessDB实时/时序数据库——ODBC之连接数据库

目录 前言 一、安装ProcessDB-ODBC驱动 1.下载ProcessDB-ODBC驱动 2.安装ProcessDB-ODBC驱动 二、配置ProcessDB数据源 三、JAVA连接ProcessDB数据库 前言 ProcessDB实时/时序数据库支持ODBC连接数据库&#xff0c;接下来将和大家分享下如何使用ODBC操作ProcessDB实时/时…

Java基础之《netty(5)—NIO之Selector》

一、基本介绍 1、Java的NIO&#xff0c;用非阻塞的IO方式&#xff0c;可以用一个线程&#xff0c;处理多个的客户端连接&#xff0c;就会使用到Selector&#xff08;选择器&#xff09;。 2、Selector能够检测多个注册的通道上是否有事件发生&#xff08;注意&#xff1a;多个…

[附源码]SSM计算机毕业设计新闻发布和评论管理系统JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

【冰糖Python】Python 中的 assert 语句

assert 判断一个表达式的真假&#xff0c;在表达式条件为 false 的时候触发异常&#xff0c;返回错误 具体用法&#xff1a; assert expression assert expression [, arguments] 实际用例&#xff1a; 注意以上使用&#xff1a; 1、条件为True时&#xff0c;assert不执行…

基于PHP+MySQL高校教务选课系统的设计与实现

兴趣是最好的老师,只有学生选择了自己感兴趣的课程才能够更好的进行学习,目前有很多高校的选课中出现很多问题,如学生对开设的课程不了解,代选课程等等,这些问题多而繁杂,不容易解决。 本系统就是为了学生开放的在线选课系统,而网络选课系统是帮助学生了解到所学课程的内容,多自…

Android——使用ContentProvider共享数据

实验名称&#xff1a; 使用ContentProvider共享数据 实验目的&#xff1a; &#xff08;1&#xff09;能使用ContentProvider共享数据 &#xff08;2&#xff09;能使用内容观察者观察其他程序的数据变化 实验内容及原理&…