【Java设计模式】十八、观察者模式

文章目录

  • 1、观察者模式
  • 2、案例:模拟微信公众号
  • 3、总结
  • 4、在源码中的实际应用

1、观察者模式

发布订阅模式,一对多,让多个观察者对象同时监听某一个主题对象。主题对象状态发生变化时,通知所有的观察者对象,让它们自动更新自己。角色:

  • 抽象主题:把所有观察者对象保存在一个集合中(可在具体主题中),并提供新增、删除观察者的方法
  • 具体主题:具体主题内部状态变化时,给所有注册过的观察者发通知
  • 抽象观察者:定义一个更新接口,给主题类调用
  • 具体观察者:实现抽象观察者,重写主题对象变更时,更新自身状态的逻辑

2、案例:模拟微信公众号

公众号更新文章,通知所有微信用户的关注者。

在这里插入图片描述
定义抽象主题类,里面包含添加、删除观察者对象的方法:

public interface Subject {
    //增加订阅者对象
    public void attach(Observer observer);

    //删除订阅者对象
    public void detach(Observer observer);
    
    //通知订阅者更新消息
    public void notify(String message);
}

定义具体的主题类:微信公众号,里面定义一个存放所有观察者对象的集合:

public class SubscriptionSubject implements Subject {

    //储存订阅公众号的微信用户
    private List<Observer> weixinUserlist = new ArrayList<Observer>();

    @Override
    public void attach(Observer observer) {
        weixinUserlist.add(observer);
    }

    @Override
    public void detach(Observer observer) {
        weixinUserlist.remove(observer);
    }

    @Override
    public void notify(String message) {
        for (Observer observer : weixinUserlist) {
            observer.update(message);  //调用观察者的update方法,里面走每个具体观察者自己的更新逻辑
        }
    }
}

定义抽象观察者类,里面定义一个更新的方法:

public interface Observer {
    void update(String message);   //主题类对象发生改变时,通知观察者对象去更新,给主题类去调用的
}

定义具体观察者类,实现更新的方法,写自己的更新逻辑:

public class WeixinUser implements Observer {
    // 微信用户名
    private String name;

    public WeixinUser(String name) {
        this.name = name;
    }
    @Override
    public void update(String message) {
    	//按需写主题对象变化时,自己的更新逻辑
        System.out.println(name + "-" + message);
    }
}

测试类:

public class Client {
    public static void main(String[] args) {
        SubscriptionSubject mSubscriptionSubject=new SubscriptionSubject();
        //创建微信用户
        WeixinUser user1=new WeixinUser("甲");
        WeixinUser user2=new WeixinUser("乙");
        WeixinUser user3=new WeixinUser("丙");
        //订阅公众号,即把观察者对象加入到主题类的观察者集合中
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        //公众号更新发出消息给订阅的微信用户
        mSubscriptionSubject.notify("主题对象更新了");
    }
}

运行:

-主题对象更新了
乙-主题对象更新了
丙-主题对象更新了

3、总结

优点:

  • 降低了主题和观察者的耦合,没有完全解耦合,但面向抽象。当新增主题时或者观察者,也满足开闭
  • 实现了广播机制

缺点:

  • 观察者很多时,集合中排后面的观察者收到消息的耗时较长

适用场景:

  • 对象之间存在一对多关系 + 一个对象状态的改变会影响到其他对象

4、在源码中的实际应用

JDK中, java.util.Observable是抽象主题类, java.util.Observer是抽象观察者类。写它们的子类,就可以实现观察者模式。

抽象主题类,里面有一个Vector集合,并在无参构造中完成了初始化。相关方法:

  • addObserver方法:添加观察者

在这里插入图片描述

  • notifyObservers方法:调用观察者对象的update方法,当内部标志为true时更新,且clearChanged方法将标志位复原

在这里插入图片描述

  • setChange方法:改变内部标志,change为true时,通知观察者

在这里插入图片描述

举例:用JDK的抽象主题和抽象观察者,写子类模拟警察抓小偷。小偷被观察的,是具体主题,注意代表着主题对象发生变化的steal方法,里面调用父类的setChange方法改变状态位,并调notifyObservers方法通知所有观察者

public class Thief extends Observable {

    private String name;

    public Thief(String name) {
        this.name = name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
	
	//代表着主题发生了变化,需要通知,那就改变标志位
    public void steal() {
        System.out.println("小偷:我偷东西了,有没有人来抓我!!!");
        super.setChanged(); //changed  = true !!
        super.notifyObservers();  //!!
    }
}

警察是一个观察者,所以需要让其实现Observer接口,重写update方法

public class Policemen implements Observer {

    private String name;

    public Policemen(String name) {
        this.name = name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("警察:" + ((Thief) o).getName() + ",我已经盯你很久了");
    }
}

测试:

public class Client {
    public static void main(String[] args) {
        //创建主题对象
        Thief t = new Thief("隔壁老王");
        //创建观察者对象
        Policemen p = new Policemen("小李");
        //让警察盯着小偷(观察者加入主题的观察者集合中)
        t.addObserver(p);
        //小偷偷东西(主题对象发生变化)
        t.steal();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot功能模块之实现单文件、多文件上传和下载

一、单文件上传 1.1MultipartFile MultipartFile 是 Spring 框架中的一个接口&#xff0c;主要用于处理 HTTP 请求中的文件上传。它提供了一些方法来获取上传文件的信息和内容。 以下是 MultipartFile 的一些主要方法&#xff1a; getOriginalFilename()&#xff1a;返回上…

vue 实现滚动导航

效果 实现 css 使用了 unocss 使界面滚动到给定元素的指定坐标位置 window.scrollTo({ top: 0 })使用了内边距避免最后数据高度不够 <main class"pb-100vh"></main>完整代码 <script lang"ts" setup> defineOptions({ name: DemoVi…

掌握Go语言:深入encoding/gob包的高效数据序列化

掌握Go语言&#xff1a;深入encoding/gob包的高效数据序列化 引言理解Gob和它的使用场景Gob的概念和设计目标Gob的适用场景和优势 开始使用Gob基本的Gob编码和解码示例代码编码&#xff08;序列化&#xff09;解码&#xff08;反序列化&#xff09; Gob编码高级应用自定义类型的…

Minecraft 1.20.1 Forge服务器保姆级搭建教程 (使用mcsm面板 | 两种启动方式)

MC Forge 服务器部署 使用 Linux 云服务器部署 Minecraft 1.20.1 Forge 服务器 目录 MC Forge 服务器部署看这篇教程之前&#xff0c;你需要准备好这篇教程的目标前期准备1. 环境需求&#xff1a;腾讯云、CentOS 7.9&#xff08;对于其他云服务商和 Linux 系统&#xff0c;操作…

[vscode]使用cmake时将命令行参数传递给调试目标

一、简介 本文介绍了在vscode中使用cmake工具时&#xff0c;如何传递参数给编译目标的方法。 前提&#xff1a;使用vscodecmake编译C/C程序。 二、方法 在.vscode/目录下新建settings.json文件&#xff0c;并将待传底的参数写在 cmake.debugConfig里。 下面介绍了一个示例&a…

重磅!2014-2023七大顶刊中国学者排行榜出炉,施一公、颜宁位列前二

在科学领域&#xff0c;Cell、Nature和Science被公认为最顶尖的期刊&#xff0c;简称CNS。而在医学方面&#xff0c;新英格兰医学杂志(NEJM)、柳叶刀(Lancet)、美国医学会杂志(JAMA)以及英国医学期刊(BMJ)被誉为“四大医学顶刊”。这七大期刊上发表的论文&#xff0c;往往被看作…

平台工程:构建企业数字化转型的基石

有人说&#xff0c;平台工程&#xff08;Platform Engineering&#xff09;&#xff0c;不过是新瓶装旧酒&#xff08;DevOps&#xff09;。 而Gartner 将平台工程列为 2024 顶级战略技术趋势之一。我国信通院也开始陆续制定与平台工程相关的技术标准。 随着数字化浪潮的席卷…

2024年软考计划开始了,你准备好了吗?

目录标题 2024年度计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试工作计划计算机软考中级科目哪个含金量最高&#xff1f;报考流程和说明 2024年度计算机技术与软件专业技术资格&#xff08;水平&#xff09;考试工作计划 一、2024年度计算机软件资格考试(初级…

数学建模-多目标规划算法(美赛建模)

单目标优化的情况下&#xff0c;只有一个目标&#xff0c;任何两解都可以依据单一目标比较其好坏&#xff0c;可以得出没有争议的最优解。 多目标化与传统的单目标优化相对。多目标优化的概念是在某个情景中在需要达到多个目标时&#xff0c;由于容易存在目标间的内在冲突&…

MO干货 | shuffle执行计划解析(下篇)

作者&#xff1a;倪涛 MO产品布道师 目录 Part 1.如何处理不均匀数据&#xff1f; Part 2.Hybrid shuffle Part 3.Shuffle resue Part 4.Join reorder Part 5.总结 在《MO干货&#xff5c;shuffle执行计划解析&#xff08;上篇&#xff09;》中&#xff0c;我们分享了shu…

选课模块-01添加免费/收费选课

添加选课 界面原型 第一步&#xff1a;用户通过搜索课程、课程推荐等信息进入课程详情页面&#xff0c;点击马上学习进行学习 第二步&#xff1a;课程免费时可以直接加入我的课程表并且免费课程可以直接在线学习&#xff0c;免费课程默认一年有效期&#xff0c;到期需要申请续…

148个Chatgpt关键词汇总-有爱AI实战教程(二)

演示站点&#xff1a; https://ai.uaai.cn 技能模块 官方论坛&#xff1a; www.jingyuai.com 京娱AI 导读&#xff1a;在使用 ChatGPT 时&#xff0c;当你给的指令越精确&#xff0c;它的回答会越到位&#xff0c;举例来说&#xff0c;假如你要请它帮忙写文案&#xff0c;如…

C语言经典算法学习-4

文章目录 21.最大访客数22.中序式转后序式&#xff08;前序式&#xff09;23.后序式的运算24.洗扑克牌&#xff08;乱数排列&#xff09;25.Craps赌博游戏 21.最大访客数 说明&#xff1a;现将举行一个餐会&#xff0c;让访客事先填写到达时间与离开时间&#xff0c;为了掌握座…

2024年Vue3 面试题小总结

Vue3 面试题小总结 1. OptionsAPI 与 CompositionAPI 的区别&#xff1f; OptionsAPI&#xff1a; 选项式API&#xff0c;通过定义data、computed、watch、method等属性与方法&#xff0c;共同处理页面逻辑&#xff1b;缺点&#xff1a; 当组件变得复杂的时候&#xff0c;导致…

视频分割软件,到底哪一款才适合你?

在当今充满创意的数字时代&#xff0c;视频编辑已成为许多人表达想法、分享故事的重要手段。而在视频编辑的过程中&#xff0c;分割视频是一项关键而常见的任务&#xff0c;它能够让我们更精细地处理内容&#xff0c;使得最终的作品更为生动和引人入胜。然而&#xff0c;要想高…

揭秘财务数据分析的五力分析,轻松实现从会计财务到管理财务的华丽转身

在这个信息爆炸的时代&#xff0c;财务数据分析已经成为了企业和个人成功的关键。今天&#xff0c;就让我们一起揭开财务数据分析的神秘面纱&#xff0c;让你轻松掌握财务秘籍&#xff0c;成为财务高手&#xff01; 一、财务数据分析&#xff0c;为何如此重要&#xff1f; 财…

访客到了官网就跳走,概率是官网颜值和体验出了问题。

很多小伙伴反馈官网ip不错&#xff0c;但是pv太少了&#xff0c;停留时间更少&#xff0c;这大概率是网站颜值和体验出问题了。 如果访客到了官网后就跳走&#xff0c;有可能是因为官网的颜值和用户体验出了问题。这种情况可能会导致访客对网站的第一印象不佳&#xff0c;从而选…

【spring】使用阿里Spring Initailiz创建项目

网络原因使用Spring Initailiz会出现超时。 那我们就换成阿里的 先看看spring官网的 网址&#xff1a;https://start.spring.io 使用一下阿里的 网址&#xff1a;https://start.aliyun.com/ 填写信息 都是java开发者&#xff0c;具体信息部介绍了。 选择组件 lombok spri…

OKHttpRetrofit

完成一个get请求 1.导入依赖 implementation("com.squareup.okhttp3:okhttp:3.14.")2.开启viewBinding android.buildFeatures.viewBinding true 3.加网络权限 和 http明文请求允许配置文件 <?xml version"1.0" encoding"utf-8"?> &l…

Kotlin:内联类(inline class)

点击查询内联类中文文档 点击查询内联类英文文档 简介 提醒&#xff1a;内联类仅在 Kotlin 1.3 之后版本可用 有时候&#xff0c;业务逻辑需要围绕某种类型创建包装器。然而&#xff0c;由于额外的堆内存分配问题&#xff0c;它会引入运行时的性能开销。此外&#xff0c;如果…