Head First设计模式(阅读笔记)-02.观察者模式

news2025/1/16 3:00:53

气象监测应用

建立一个应用,利用WeatherData对象取得气象站的数据,并更新三个布告板:目前状况、气象统计和天气预报


要求


  • WeatherData类具有getter方法获取温度、湿度和气压
  • 获取到新的数据时会调用measurementsChanged方法
  • 当有新数据时三个布告板需要更新
  • 系统需要具有扩展性,比如添加新的布告板

简单实现

未使用设计模式的代码如下,存在许多问题:

public class WeatherData{
	public void measurementsChanged(){
        // 获取最新数据
        float temp = getTemperature();
        float humidity = getHumidity();
        float pressure = getPressure();
        
        // 更新布告板
        currentConditionDisplay.update(temp, humidity, pressure);
        statisticDisplay.update(temp, humidity, pressure);
        forcecastDisplay.update(temp, humidity, pressure);
    }
    // 其他方法
}

  • 针对具体实现编程,意味着在后续增加或删除布告板时需要修改程序
  • 没有封装改变的部分,比如更新布告板的实现代码

观察者模式


从订阅报纸入手

先了解下报纸的订阅/取消订阅过程:

  • 当你向某家报社订阅报纸后,每次它们出版新报纸时就会送一份给你
  • 如果你不想在看报纸就可以取消订阅,报社也就不再送了

在观察者模式中,报纸这样的出版者称为主题,我们这样的订阅者称为观察者:

  • 主题对象管理数据,当主题内的数据改变时就会通知观察者
  • 观察者订阅(注册)主题后,每当主题数据改变时能收到更新
  • 同时每个观察者也可以把自己从该主题的观察者集合中删除

定义观察者模式

观察者模式定义了对象之间一对多的依赖,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新


在这里插入图片描述

观察者模式的优势

让主题和观察者之间松耦合


  • 主题只知道观察者实现了Observer接口,它并不需要知道具体观察者是谁以及做了什么
  • 主题唯一依赖的东西是一个实现Observer接口的对象列表,所以任何时候都可以新增/删除观察者
  • 有新的观察者出现时,主题的代码也无需修改,新的类只需要实现观察者接口并且注册为观察者即可
  • 可以单独复用主题或观察者,因为它们非紧耦合

重新设计应用


实现气象站


// 主题接口
public interface Subject{
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

// 观察者接口
public interface Observer{
    // 这些数值发生变化,主题就会把新数值作为参数传递给观察者
    public void update(float temp, float humidity, float pressure);
}

// 展示内容接口
public interface DisplayElment{
	public void display();  // 布告板需要显示时就调用该方法
}

在WeatherData中实现主题接口


// 具体的主题类WeatherData
public class WeatherData implements Subject{
    private ArrayList observers;  // 记录观察者,在构造器中初始化
    private float temperature;
    private float humidity;
    private float pressure;
    
    public WeatherData(){
        observers = new ArrayList();
    }
    
    public void registerObserver(Observer o){
        observers.add(o);  // 观察者的注册即加入到观察者列表中即可
    }
    public void removeObserver(Observer o){
        int i = observers.indexOf(o);
        if(i >= 0){
            Observers.remove(i);  // 观察者的删除即从列表中移除即可
        }
    }
    public void notifyObservers(){
    	for(int i = 0; i < observers.size(); i++){
            Observer Observer = (Observer)observers.get(i);
            // 调用列表中每个Observer的update方法
            Observer.update(temperature, humidity, pressure);
        }   
    }   
    
    public void measurementsChanged(){  // 在最初的要求中提到了使用该方法进行更新
        notifyObservers();
    }
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
}

建立布告板

以目前状况布告板为例,其他两个类似


public class CurrentConditionsDisplay implements Observer, DisplayElement{
    private float temperature;
    private float humidity;
    private Subject weatherData;
    
    public CurrentConditionsDisplay(Subject weatherData){
        // 目前状况布告板需要把自己注册给weatherData
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }
    
    public void update(float temperature, float humidity, float pressure){
        // 目前状况布告板只需要temperature和humidity
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }
    public void display(){
        System.out.println("目前状况: " + temperature + "度 " + humidity);
    }
}

启动


public class WeatherStation{
    public static void main(String[] args){
        WeatherData weatherData = new WeatherData();
        CurrentConditionsDisplay c = new CurrentConditionsDisplay(weatherData);
        weatherData.setMeasurements(10,10,10.3f);
    }
}

Java内置的观察者模式

在上述操作中每次都是主题去送所有数据给注册好的观察者们,但是并不是每个观察者需要所有数据,为什么不能让观察者自己选择取需要的数据呢?Java API中内置的观察者模式就实现了两种方式传递数据


在WeatherData中继承Observable


import java.util.Observable;
public class WeatherData extends Observable{
    private float temperature;
    private float humidity;
    private Subject weatherData;
    public WeatherData(){} // 因为这里实现拉取方式,WeatherData也就不需要记录观察者们
    
    // 使用推方式的时候需要使用setMeasurements和measurementsChanged
    public void measurementsChanged(){
        // 该方法用于标记状态状态已经改变,让notifyObservers方法知道该方法被调用时应该更新观察者
        // 这样就不会出现每次更新一点数据就通知观察者,可以设置每次更新到某个阈值再去调用setChanged方法
        setChanged();  
        notifyObservers();
    }
    
    public void setMeasurements(float temperature, float humidity, float pressure){
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        measurementsChanged();
    }
    
    // getter用于拉方式的时候使用
    public float getTemperature(){
        return temperature;
    }
    public float getHumidity(){
        return humidity;
    }
    public float getPressure(){
        return pressure;
    }
}

重新建立布告板


import java.util.Observable;
import java.util.Observer;
public class CurrentConditionsDisplay implements Observer, DisplayElement{
    Observable Observable;
    private float temperature;
    private float humidity;
    
    public CurrentConditionsDisplay(Observable observable){
        // 目前状况布告板需要把自己注册给weatherData
        this.observable = observable;
        observable.addObserver(this);
    }
    
    // 主题作为第一个变量,好让观察者知道是哪个主题通知它
    public void update(Observable obs, Object arg){
        if(obs instanceof WeatherData){
            WeatherData weatherData = (WeatherData)obs;
            this.temperature = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            display();
        }
    }
    public void display(){
        System.out.println("目前状况: " + temperature + "度 " + humidity);
    }
}

缺点

java.util.ObservableJDK9之后已被标记为过时类


  • java.util.Observable是一个类并非接口,由于Java的单继承机制,所以限制了它的复用
  • setChanged方法被protected修饰,所以要创建Observable实例并组合到自己对象中就必须继承Observable(不是很明白),违法了多用组合,少用继承的设计原则

参考

Head First 设计模式-观察者模式

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

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

相关文章

WebVR

WebVR 文章目录WebVR1. 前言2. 在线示例3. 关于开发4. 参考链接1. 前言 WebVR技术可以实现在浏览器端接通VR设备&#xff0c;在VR模式下进行交互。 2. 在线示例 三维模型网站Sketchfab支持在VR模式下预览三维模型&#xff1a;https://sketchfab.com/3d-models/kirkaldys-tes…

从后端角度看安全

跨站脚本攻击&#xff08;XSS&#xff09; 什么是XSS 跨站脚本工具&#xff0c;全程是Cross Site Script&#xff0c;为了和CSS 区分&#xff0c;所以叫XSS。 XSS 攻击&#xff0c;通常指黑客通过HTML注入&#xff0c;来纂改了网页&#xff0c;插入恶意脚本。 人话就是把用户…

OTG 配置为U盘

目录 硬件环境及目标 配置脚本 问题1 &#xff0c;创建g1 目录失败 问题1 的解决 问题2 &#xff0c;目录不存在 访问存储卡 综述 网上很多资料介绍通过configfs将OTG 口配置为U盘的资料&#xff0c;本文记录实际操作及遇到的问题。 硬件环境及目标 硬件基本结构如下图。 …

打造高逼格、可视化的监控系统平台

1、安装influxdb数据库 docker run -d --name influxdb -p 8083:8083 -p 8086:8086 tutum/influxdb 两个端口都要映射出来&#xff0c;同时要开启防火墙端口 web http://192.168.199.151:8083/进入8083端口&#xff0c;创建数据库cadvisor 创建管理员admin密码123456的用户…

windows glog 安装以及环境搭建

ocr 代码是用 C 写得&#xff0c;以前只有一个同事在维护&#xff0c;他离职几年了&#xff0c;那块代码没人能改得了。工作后一直在写 Java &#xff0c;写了快 10 年 Java 了。看了几天 C 代码&#xff0c;终于能在 vs 下把代码给跑起来了。 写 Java 代码是在 mac 平台下 In…

uniapp 切换WIFI

最近有一个新的需求&#xff0c;在app中实现切换wifi的功能。 方法一、 实现&#xff1a;使用安卓9&#xff08;28&#xff09;方法的WifiManager.addNetwork切换&#xff0c;具体实现方法大家自己搜&#xff0c;很容易搜到。 弊端&#xff1a; 要先忘记 WI-FI 才能连接成功&…

竞品分析:秒健康

发展历程 妙健康产品上线后&#xff0c;最初主要提供相关的在线医疗服务健康管理社区互动&#xff0c;虽然也有瘦身等相关功能&#xff0c;但整体业务偏向医疗化&#xff0c;用户年龄层也相对偏高。 15年进入健康市场后&#xff0c;不断探索并完善有关医疗业务的相关功能&am…

FBA海运是什么,FBA海运的优势是什么

FBA的方式分为海运&#xff0c;空运&#xff0c;快递这种&#xff0c;其中海运是各种方式中性价比最高的一种&#xff0c;不仅价格便宜&#xff0c;而且运货量大&#xff0c;清关简单&#xff0c;虽然时效有点慢&#xff0c;但是量大可以补救&#xff0c;我们在这里主要说一说F…

java设计模式

设计模式汇总(copy) 介绍 内容链接设计模式简介https://blog.csdn.net/m0_54485604/article/details/113309133 UML 类图介绍 内容链接UML类图https://blog.csdn.net/m0_54485604/article/details/113243818 六大设计原则 内容链接开闭原则https://blog.csdn.net/m0_54485604/a…

Spring:AOP的核心概念(10)

AOP核心概念AOP简介什么是AOPAOP作用AOP核心概念AOP入门案例AOP工作流程流程1: Spring容器启动流程2: 读取所有切面配置中的切入点流程3: 初始化bean流程4: 获取bean执行方法AOP核心概念AOP小结AOP简介 什么是AOP AOP(Aspect Oriented Programming)面向切面编程&#xff0c;一…

SSD 1306显示屏 adafruit SSD 1306

文章目录1.常用函数1.字体oled.printoled.setRotation(1);oled.setTextSize(1);oled.setCursor(35, 5);2.图形类oled.fillScreen(WHITE );//coloroled.fillRect(10, 10, 20, 20, WHITE );//x y x1 y1 coloroled.drawRect(10, 10, 40,40, WHITE );//x y x1 y1 coloroled.drawCir…

数字信号处理-3-函数的正交

0 导读 如果函数成正交关系&#xff0c;那么它们的积的定积分为 0。反过来说就是&#xff0c;如果两个函数相乘的定积分值为 0&#xff0c;那么称这两个函数正交。sinx 与 cosx 正交&#xff0c;sinnx 与 sinmx 正交&#xff08;m与n不相等&#xff09;&#xff0c;cosnx 与 c…

留学文书Statement of Purpose写法介绍

留学目的陈述&#xff08;Statement of Purpose, 通常简写为SOP&#xff09;是留学申请文书里的重要内容之一。通过SOP&#xff0c;目标学校可以更深入地了解申请人的留学目的以及申请人的目标是否和学校的教学理念相一致。因此&#xff0c;在撰写SOP的时候&#xff0c;申请者要…

开播客户端

OBS架构 配置数据&#xff1a;json表示 libobs接口导出&#xff1a;export、import 多线程中的缓冲队列&#xff1a;circlebuf动态循环缓冲 分层 UI > libobs > 插件 libobs C实现的&#xff0c;拥有一个全局变量控制所有事务。 视频采集渲染线程、视频编码线程&…

基于PHP+MYSQL的网上鲜花店销售系统(含论文)

鲜花在人们的生活中是一个非常重要的东西,在节假日或者一些有特殊含义的日子里人们通常会给亲朋好友或者长辈们送上一束有特殊含义的鲜花来表示自己对节日的问候,为了给人们增加鲜花的选择性和降低购买的难度我们开发了本网上鲜花店销售系统 本网上鲜花店销售系统是通过当下最流…

CorelDRAW2023安装下载教程精简版矢量绘图软件

CDR全称是CorelRAW2023&#xff0c;它不同于Photoshop&#xff0c;PS是一款图片处理软件&#xff0c;而CDR是一款较为常用的矢量绘图设计软件&#xff0c;该软件给设计师提供了矢量动画、页面设计、网站制作、位图编辑和网页动画等多种功能&#xff0c;使用的比较多的版本是202…

【读点论文】CMT: Convolutional Neural Networks Meet Vision Transformers

CMT: Convolutional Neural Networks Meet Vision Transformers Abstract 视觉transformer已经成功地应用于图像识别任务&#xff0c;因为它们能够捕获图像中的长距离依赖性。然而&#xff0c;transformer和现有卷积神经网络&#xff08;CNN&#xff09;在性能和计算成本方面仍…

Pandas太慢?快使用Vaex DataFrame,每秒数亿数据算起来 ⛵

&#x1f4a6; 作者&#xff1a;韩信子ShowMeAI &#x1f4d8; 数据分析实战系列&#xff1a;https://www.showmeai.tech/tutorials/40 &#x1f4d8; 本文地址&#xff1a;https://www.showmeai.tech/article-detail/393 &#x1f4e2; 声明&#xff1a;版权所有&#xff0c;转…

LR低代码快速开发平台 高效调整企业组织架构

组织架构以及围绕组织架构的设计、实施和变革&#xff0c;是企业管理永恒的话题&#xff0c;它上承公司的业务战略和运营模式&#xff0c;下接业务流程和信息系统建设&#xff0c;重要性不言而喻。数字化变革浪潮之下&#xff0c;商业模式的颠覆、价值链的重塑都需要由相匹配的…

解读商业智能BI,数据仓库中的元数据

之前的文章讨论过数据分析、数据治理、数据仓库等等&#xff0c;即使是非业内人员从字面意思&#xff0c;也是可以了解一二的&#xff0c;但是&#xff0c;很多人对于元数据可能就比较陌生了。那么&#xff0c;今天我们就来聊一聊元数据管理。 一、数据仓库 要说元数据&#…