【设计模式——学习笔记】23种设计模式——观察者模式Observer(原理讲解+应用场景介绍+案例介绍+Java代码实现)

文章目录

  • 案例引入
    • 原始方案实现
      • 实现
      • 问题分析
  • 介绍
    • 基础介绍
    • 登场角色
  • 案例实现
    • 案例一
      • 类图
      • 实现
      • 分析
    • 案例二
      • 类图
      • 实现
  • 观察者模式在JDK源码的应用
  • 总结
  • 文章说明

案例引入

有一个天气预报项目,需求如下:

  • 气象站可以将每天测量到的温度、湿度、气压等等以公告的形式发布出去(比如发布到自己的网站或第三方)
  • 需要设计开放型API,便于其他第三方也能接入气象站获取数据
  • 提供温度、气压、湿度的接口
  • 测量数据更新时,要能实时的通知给第三方

原始方案实现

设计一个WeatherData类,类里面的方法如下:

  • getTemperature0:获取温度
  • getHumidity0:获取湿度
  • getPressure0:获取气压
  • dataChange0:可以定时(如每十分钟调用一次)调用该方法去气象站更新温度、湿度、气压数据,并主动推送给应用者

实现

【当前天气状况】

package com.atguigu.observer;/*** 显示当前天气情况(可以理解成是气象站的网站)* @author Administrator**/
public class CurrentConditions {/*** 温度*/private float temperature;/*** 气压*/private float pressure;/*** 湿度*/private float humidity;/*** 更新 天气情况,是由WeatherData来调用将最新的数据推送过来* @param temperature* @param pressure* @param humidity*/public void update(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;display();}/*** 显示天气情况*/public void display() {System.out.println("***Today mTemperature: " + temperature + "***");System.out.println("***Today mPressure: " + pressure + "***");System.out.println("***Today mHumidity: " + humidity + "***");}
}

【气象台的天气数据】

package com.atguigu.observer;/*** 类是核心* 1. 包含最新的天气情况信息* 2. 含有 CurrentConditions 对象* 3. 当数据有更新时,就主动的调用CurrentConditions对象的update方法(含 display), 这样他们(接入方)就看到最新的信息* @author Administrator**/
public class WeatherData {private float temperatrue;private float pressure;private float humidity;private CurrentConditions currentConditions;/*** 加入新的第三方* @param currentConditions*/public WeatherData(CurrentConditions currentConditions) {this.currentConditions = currentConditions;}public float getTemperature() {return temperatrue;}public float getPressure() {return pressure;}public float getHumidity() {return humidity;}public void dataChange() {//调用 接入方的 updatecurrentConditions.update(getTemperature(), getPressure(), getHumidity());}/*** 当数据有更新时,就调用 setData* @param temperature* @param pressure* @param humidity*/public void setData(float temperature, float pressure, float humidity) {this.temperatrue = temperature;this.pressure = pressure;this.humidity = humidity;//调用dataChange, 将最新的信息 推送给 接入方 currentConditionsdataChange();}
}

【主类】

package com.atguigu.observer;public class Client {public static void main(String[] args) {//创建接入方 currentConditionsCurrentConditions currentConditions = new CurrentConditions();//创建 WeatherData 并将 接入方 currentConditions 传递到 WeatherData中WeatherData weatherData = new WeatherData(currentConditions);//更新天气情况weatherData.setData(30, 150, 40);//天气情况变化System.out.println("============天气情况变化=============");weatherData.setData(40, 160, 20);}
}

【运行】

***Today mTemperature: 30.0***
***Today mPressure: 150.0***
***Today mHumidity: 40.0***
============天气情况变化=============
***Today mTemperature: 40.0***
***Today mPressure: 160.0***
***Today mHumidity: 20.0***Process finished with exit code 0

问题分析

当添加其他第三方网站时,需要修改WeatherData类,不符合开闭原则,不利于维护

在这里插入图片描述

改进:推荐使用观察者模式

介绍

基础介绍

  • 观察者模式的工作原理:观察者到观察对象中进行注册,当观察对象的状态发生改变时,就通知已经注册的观察者,观察者可以被从观察对象中移除

登场角色

在这里插入图片描述

  • Subject(观察对象):Subject角色定义了注册、删除观察者的方法,还可以声明“获取现在的状态”的方法
  • ConcreteSubject(具体的观察对象):当自身状态发生变化后,它会通知所有已经注册的Observer角色
  • Observer(观察者):负责接收来自Subject角色状态变化的通知,声明了update方法来接收通知
  • ConcreteObserver(具体的观察者):当它的update方法被调用后,会去获取要观察的对象的最新状态

案例实现

案例一

类图

在这里插入图片描述

实现

【Subject:观察对象接口】

package com.atguigu.observer.improve;/*** 接口, 让WeatherData 来实现*/
public interface Subject {/*** 观察者注册* @param o*/public void registerObserver(Observer o);/*** 移除观察者* @param o*/public void removeObserver(Observer o);/*** 通知所有观察者*/public void notifyObservers();
}

【Observer:观察者接口】

package com.atguigu.observer.improve;/*** 观察者接口*/
public interface Observer {/*** 更新数据** @param temperature* @param pressure* @param humidity*/public void update(float temperature, float pressure, float humidity);
}

【WeatherData:具体观察对象】

package com.atguigu.observer.improve;import java.util.ArrayList;/*** 类是核心* 1. 包含最新的天气情况信息* 2. 含有 观察者集合,使用ArrayList管理* 3. 当数据有更新时,就主动的调用ArrayList, 通知所有的(接入方)就看到最新的信息** @author Administrator*/
public class WeatherData implements Subject {private float temperature;private float pressure;private float humidity;/*** 观察者集合*/private ArrayList<Observer> observers;/*** 构造方法*/public WeatherData() {// 创建新的集合observers = new ArrayList<Observer>();}public float getTemperature() {return temperature;}public float getPressure() {return pressure;}public float getHumidity() {return humidity;}public void dataChange() {//调用 接入方的 updatenotifyObservers();}//当数据有更新时,就调用 setDatapublic void setData(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;//调用dataChange, 将最新的信息 推送给 接入方 currentConditionsdataChange();}/*** 注册一个观察者* @param o*/@Overridepublic void registerObserver(Observer o) {observers.add(o);}/*** 移除一个观察者* @param o*/@Overridepublic void removeObserver(Observer o) {if (observers.contains(o)) {observers.remove(o);}}/*** 遍历所有的观察者,并通知*/@Overridepublic void notifyObservers() {for (int i = 0; i < observers.size(); i++) {observers.get(i).update(this.temperature, this.pressure, this.humidity);}}
}

【CurrentConditions:具体观察者】

package com.atguigu.observer.improve;public class CurrentConditions implements Observer {/*** 温度*/private float temperature;/*** 气压*/private float pressure;/*** 湿度*/private float humidity;/*** 更新 天气情况,是由 WeatherData 来调用,我使用推送模式** @param temperature* @param pressure* @param humidity*/public void update(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;display();}/*** 显示*/public void display() {System.out.println("***Today mTemperature: " + temperature + "***");System.out.println("***Today mPressure: " + pressure + "***");System.out.println("***Today mHumidity: " + humidity + "***");}
}

【BaiduSite:具体观察者】

package com.atguigu.observer.improve;/*** 百度网站*/
public class BaiduSite implements Observer {private float temperature;private float pressure;private float humidity;/*** 更新天气情况,是由 WeatherData 来调用,我使用推送模式** @param temperature* @param pressure* @param humidity*/public void update(float temperature, float pressure, float humidity) {this.temperature = temperature;this.pressure = pressure;this.humidity = humidity;display();}/*** 显示*/public void display() {System.out.println("===百度网站====");System.out.println("***百度网站 气温 : " + temperature + "***");System.out.println("***百度网站 气压: " + pressure + "***");System.out.println("***百度网站 湿度: " + humidity + "***");}}

【主类】

package com.atguigu.observer.improve;public class Client {public static void main(String[] args) {//创建一个WeatherDataWeatherData weatherData = new WeatherData();//创建观察者CurrentConditions currentConditions = new CurrentConditions();BaiduSite baiduSite = new BaiduSite();//注册到weatherDataweatherData.registerObserver(currentConditions);weatherData.registerObserver(baiduSite);//测试System.out.println("通知各个注册的观察者, 看看信息");weatherData.setData(10f, 100f, 30.3f);System.out.println();System.out.println("----- 移除一个观察者 -----");weatherData.removeObserver(currentConditions);//测试System.out.println();System.out.println("通知各个注册的观察者, 看看信息");weatherData.setData(10f, 100f, 30.3f);}}

【运行】

通知各个注册的观察者, 看看信息
***Today mTemperature: 10.0***
***Today mPressure: 100.0***
***Today mHumidity: 30.3***
===百度网站====
***百度网站 气温 : 10.0***
***百度网站 气压: 100.0***
***百度网站 湿度: 30.3***----- 移除一个观察者 -----通知各个注册的观察者, 看看信息
===百度网站====
***百度网站 气温 : 10.0***
***百度网站 气压: 100.0***
***百度网站 湿度: 30.3***Process finished with exit code 0

分析

  • 使用该模式,拓展方便,比如需要新增一个观察者,只需要实现一个具体的观察者类,然后在观察对象中注册即可

案例二

类图

在这里插入图片描述

实现

【观察者接口】

package com.atguigu.observer.Sample;public interface Observer {public abstract void update(NumberGenerator generator);
}

【被观察对象抽象类】

package com.atguigu.observer.Sample;import java.util.ArrayList;
import java.util.Iterator;/*** 用来生成数值的抽象类*/
public abstract class NumberGenerator {/*** 保存Observer们*/private ArrayList observers = new ArrayList();/*** 注册Observer** @param observer*/public void addObserver(Observer observer) {observers.add(observer);}/*** 删除Observer** @param observer*/public void deleteObserver(Observer observer) {observers.remove(observer);}/*** 向Observer发送通知*/public void notifyObservers() {Iterator it = observers.iterator();while (it.hasNext()) {Observer o = (Observer) it.next();o.update(this);}}/*** 获取数值** @return*/public abstract int getNumber();/*** 生成数值*/public abstract void execute();
}

【具体的被观察对象】

package com.atguigu.observer.Sample;import java.util.Random;/*** 具体的被观察对象*/
public class RandomNumberGenerator extends NumberGenerator {/*** 随机数生成器*/private Random random = new Random();/*** 当前数值*/private int number;/*** 获取当前数值* @return*/public int getNumber() {                return number;}public void execute() {for (int i = 0; i < 20; i++) {// 生成随机数number = random.nextInt(50);// 通知观察者notifyObservers();}}
}

【数值显示观察者】

package com.atguigu.observer.Sample;/*** 使用数字形式显示观察到的数值*/
public class DigitObserver implements Observer {public void update(NumberGenerator generator) {System.out.println("DigitObserver:" + generator.getNumber());try {Thread.sleep(100);} catch (InterruptedException e) {}}
}

【图像显示观察者】

package com.atguigu.observer.Sample;/*** 使用图像显示观察到的数值*/
public class GraphObserver implements Observer {public void update(NumberGenerator generator) {System.out.print("GraphObserver:");int count = generator.getNumber();for (int i = 0; i < count; i++) {System.out.print("*");}System.out.println("");try {Thread.sleep(100);} catch (InterruptedException e) {}}
}

【主类】

package com.atguigu.observer.Sample;public class Main {public static void main(String[] args) {NumberGenerator generator = new RandomNumberGenerator();Observer observer1 = new DigitObserver();Observer observer2 = new GraphObserver();generator.addObserver(observer1);generator.addObserver(observer2);generator.execute();}
}

【运行】

DigitObserver:2
GraphObserver:**
DigitObserver:7
GraphObserver:*******
DigitObserver:6
GraphObserver:******
DigitObserver:26
GraphObserver:**************************
DigitObserver:16
GraphObserver:****************
DigitObserver:6
GraphObserver:******
DigitObserver:28
GraphObserver:****************************
DigitObserver:10
GraphObserver:**********
DigitObserver:6
GraphObserver:******
DigitObserver:10
GraphObserver:**********
DigitObserver:20
GraphObserver:********************
DigitObserver:14
GraphObserver:**************
DigitObserver:21
GraphObserver:*********************
DigitObserver:38
GraphObserver:**************************************
DigitObserver:31
GraphObserver:*******************************
DigitObserver:34
GraphObserver:**********************************
DigitObserver:19
GraphObserver:*******************
DigitObserver:30
GraphObserver:******************************
DigitObserver:0
GraphObserver:
DigitObserver:1
GraphObserver:*Process finished with exit code 0

观察者模式在JDK源码的应用

在这里插入图片描述

Observable直接就是类,没有实现Subject接口,直接在类中实现管理Observer的核心方法,当然,可以写更多的子类来集成Observable以便实现更多的操作

在这里插入图片描述

总结

  • 可替换性强:被观察对象中的观察者集合的观察者类型是Observer角色,可以接收各种各样的具体Observer角色,而且可以统一调用他们的update方法;Observer的update方法接收的数据类型也是Subject,在update方法里面也可以统一调用方法来获取具体被观察对象的属性
  • update方法所需要的参数非常灵活,可以自己按照需求来定义
  • 观察者模式并非主动去观察,而是被观测对象主动去通知,称为发布——订阅模式可能更加贴切

文章说明

  • 本文章为本人学习尚硅谷的学习笔记,文章中大部分内容来源于尚硅谷视频(点击学习尚硅谷相关课程),也有部分内容来自于自己的思考,发布文章是想帮助其他学习的人更方便地整理自己的笔记或者直接通过文章学习相关知识,如有侵权请联系删除,最后对尚硅谷的优质课程表示感谢。
  • 本人还同步阅读《图解设计模式》书籍(图解设计模式/(日)结城浩著;杨文轩译–北京:人民邮电出版社,2017.1),进而综合两者的内容,让知识点更加全面

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

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

相关文章

Apache Kafka Learning

目录 一、Kafka 1、Message Queue是什么&#xff1f; 2、Kafka 基础架构 3、Kafka安装 二、Maven项目测试 1、Topic API 2、生产者&消费者 一、Kafka Kafka是由Apache软件基金会开发的一个开源流处理平台&#xff0c;由Scala和Java编写。Kafka是一种高吞吐量的分布式…

Flutter 实现按位置大小比例布局的控件

文章目录 前言一、如何实现&#xff1f;1、数值转成分数2、RowFlexible布局横向3、ColumnFlexible布局纵向 二、完整代码三、使用示例1、基本用法2、四分屏3、六分屏4、八分屏5、九分屏6、414分屏 总结 前言 做视频监控项目时需要需要展示多分屏&#xff0c;比如2x2、3x3、414…

使用 LangChain 搭建基于 Amazon DynamoDB 的大语言模型应用

LangChain 是一个旨在简化使用大型语言模型创建应用程序的框架。作为语言模型集成框架&#xff0c;在这个应用场景中&#xff0c;LangChain 将与 Amazon DynamoDB 紧密结合&#xff0c;构建一个完整的基于大语言模型的聊天应用。 本次活动&#xff0c;我们特意邀请了亚马逊云科…

C#类型转换

&#x1f35f;数据类型 大体分为三个大类型&#xff1a;整型&#xff08;其中又分为有符号整型、无符号整型&#xff09;、浮点型、特殊类型 注意&#xff1a;浮点数在初始化时要在值后加上后缀&#xff0c;双精度浮点数decimal的后缀为“M”、单精度浮点数double和float的后…

AI相机“妙鸭相机”原理分析和手动实现方案

妙鸭相机 一个通过上传大约20张照片&#xff0c;生成专属自拍。在2023年7月末爆火&#xff0c;根据36Kr报道&#xff0c;妙鸭相机系阿里系产品&#xff0c;挂靠在阿里大文娱体系下&#xff0c;并非独立公司。 使用方法是上传20张自拍照片&#xff0c;之后可以选择模板生成自己…

算法通过村——Hash和队列问题解析

算法的备胎Hash和找靠山的队列 备胎Hash Hash&#xff0c;不管是算法&#xff0c;还是在工程中都会大量使用。很多复杂的算法问题都用Hash能够轻松解决&#xff0c;也正是如此&#xff0c;在算法例就显得没什么思维含量&#xff0c;所以Hash是应用里的扛把子&#xff0c;但在算…

【安全测试】安全测试威胁建模设计方法STRIDE

目录 背景 TM(ThreatModeling) 实践 具体流程 资料获取方法 背景 目前安全测试一般都存在如下问题&#xff1a; 安全测试人员不懂业务&#xff0c;业务测试人员不懂安全&#xff0c;安全测试设计出现遗漏是无法避免的安全测试点繁多复杂&#xff0c;单点分析会导致风险暴…

selenium 截屏

当前环境&#xff1a; Windows 10 Python 3.7 selenium 3.141.0 Google Chrome 115.0.5790.110 &#xff08;64 位&#xff09; from selenium import webdriver import base64if __name__ __main__:#driver webdriver.Chrome()driver.get(https://www.baidu.com/)# 1.…

简单游戏截图_可控截取内容2

一个需求 我需要在场景中截取不同层级的截图(如只截模型或只截UI或只截外部相加看到的画面 或全都截或和Shader配合呈现人眼夜视仪热成像的画面切换) 将截图排到列表中&#xff0c;在场景UI中展示出来 如何做 相机要能够看到不同的画面 将当前帧画面存储下来 将存储的画面展示出…

【2023年电赛国一必备】E题报告模板--可直接使用

任务 图1 任务内容 要求 图2 基本要求内容 图3 发挥部分内容 说明 图4 说明内容 评分标准 图5 评分内容 正文 &#xff08;部分&#xff09; 摘要 本文使用K210芯片设计了一个运动目标控制与自动追踪系统。系统包括使用深度学习进行识别激光位置&#xff0c;其中红色激…

Emacs之将.el编译成bin(一百二十五)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

vscode如何退出/切换 github 账号

退出/切换 github 账号 左下角点击头像按钮&#xff0c;选择注销&#xff0c;然后再重新登录

JS进阶-Day3

&#x1f954;&#xff1a;永远做自己的聚光灯 JS进阶-Day1——点击此处&#xff08;作用域、函数、解构赋值等&#xff09; JS进阶-Day2——点击此处&#xff08;深入对象之构造函数、实例成员、静态成员等&#xff1b;内置构造函数之引用类型、包装类型等&#xff09; 更多JS…

boost beast http server 测试

boost beast http client boost http server boost beast 是一个非常好用的库&#xff0c;带上boost的协程&#xff0c;好很多东西是比较好用的&#xff0c;以下程序使用四个线程开启协程处理进入http协议处理。协议支持http get 和 http post #include <boost/beast/cor…

Java与Kotline Funcation函数与参数函数的详解

一.介绍 在现在以IDE为开发工具的时代&#xff0c;各种开发语言都有&#xff0c;kotlin的语法势头比较强&#xff0c;今天我们将介绍在项目中出现比较多的两种函数&#xff0c;一种是参数函数&#xff0c;还有一种就是Function函数 如果你不了匿名函数请阅读以下文档&#xff…

ISC 2023︱诚邀您参与赛宁“安全验证评估”论坛

​​8月9日-10日&#xff0c;第十一届互联网安全大会&#xff08;简称ISC 2023&#xff09;将在北京国家会议中心举办。本次大会以“安全即服务&#xff0c;开启人工智能时代数字安全新范式”为主题&#xff0c;打造全球首场AI数字安全峰会&#xff0c;赋予安全即服务新时代内涵…

常见的设计模式(超详细)

文章目录 单例模式饿汉式单例模式懒汉式单例模式双重检索单例模式 工厂模式简单工厂模式工厂&#xff08;方法&#xff09;模式抽象工厂模式 原型模式代理模式 单例模式 确保一个类只有一个实例&#xff0c;并且自行实例化并向整个系统提供这个实例。 饿汉式单例模式 饿汉式单…

关于win11 debian wsl 子系统安装启动docker一直starting,无法启动

首先我先说明&#xff0c;我的步骤都是按照官网步骤来的 通过官网的操作步骤 通过测试命令 sudo docker run hello-world得到下面的命令&#xff0c;我们通过启动命令 sudo service docker start 执行结果如下图 也就是说无法启动&#xff0c;一直显示在启动中 遇到这种情况…

js实现轮播图(手动+自动)

目录 设置大体样式 图片播放 完整代码 设置大体样式 <input type"button" value"<" id"pre" onclick"pre()" onmouseover"stop()" onmouseout"start()" class"left"> <img src"..…

大数据教材推荐|Python数据挖掘入门、进阶与案例分析

主 编&#xff1a; 卢滔&#xff0c;张良均&#xff0c;戴浩&#xff0c;李曼&#xff0c;陈四德 出版社&#xff1a; 机械工业出版社 内容提要 本书从实践出发&#xff0c;结合11个“泰迪杯”官方推出的赛题&#xff0c;按照赛题的难易程度进行排序&#xff0c;由浅入深…