前端开发设计模式——观察者模式

embedded/2024/10/30 21:18:20/

目录

一、定义和特点

1. 定义

2. 特点

二、实现方式

1. 使用 JavaScript 实现观察者模式的基本结构

2. 实际应用中的实现示例

三、使用场景

1. 事件处理

2. 数据绑定

3. 异步通信

4. 组件通信

四、优点

1. 解耦和灵活性

2. 实时响应和数据一致性

3. 提高代码的可复用性

五、缺点

1. 内存泄漏风险

2. 通知顺序和复杂性

3. 性能开销

六、注意事项

1. 正确管理观察者的生命周期

2. 注意通知的频率和时机

3. 处理好观察者之间的关系

4. 测试和调试


一、定义和特点

1. 定义

        观察者模式是一种软件设计模式,它定义了对象之间的一种一对多的依赖关系。当一个对象(被观察对象,也称为主题或可观察对象)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。

2. 特点

  • 一对多关系:一个主题可以有多个观察者与之关联。主题负责通知观察者,而观察者只需关注主题的状态变化并做出相应反应,它们之间的耦合度较低。
  • 自动更新:观察者无需主动查询主题的状态,而是在主题状态变化时自动接收通知并进行更新操作,保证了数据的一致性和及时性。
  • 解耦性强:主题和观察者相互独立,它们只通过定义好的接口进行交互。这使得系统的各个模块可以独立开发、修改和扩展,增强了系统的灵活性和可维护性。

二、实现方式

1. 使用 JavaScript 实现观察者模式的基本结构

// 定义主题(可观察对象)类
class Subject {constructor() {this.observers = []; // 存储观察者的数组}// 添加观察者addObserver(observer) {this.observers.push(observer);}// 删除观察者removeObserver(observer) {const index = this.observers.indexOf(observer);if (index!== -1) {this.observers.splice(index, 1);}}// 通知观察者状态更新notifyObservers() {this.observers.forEach(observer => {observer.update(this);});}
}// 定义观察者类
class Observer {update(subject) {// 在这里实现具体的更新逻辑,不同的观察者可以有不同的实现console.log('观察者收到主题状态更新通知');}
}

2. 实际应用中的实现示例

        假设我们正在开发一个简单的天气预报应用,其中天气数据是主题,而在页面上显示天气信息的组件以及根据天气情况进行提醒的功能模块等都是观察者。

// 天气数据主题类
class WeatherData extends Subject {constructor() {super();this.temperature = 0;this.humidity = 0;}// 更新天气数据并通知观察者setWeatherData(newTemperature, newHumidity) {this.temperature = newTemperature;this.humidity = newHumidity;this.notifyObservers();}
}// 显示天气信息的观察者类
class DisplayWeatherObserver extends Observer {update(weatherData) {const temperature = weatherData.temperature;const humidity = weatherData.humidity;// 在这里更新页面上显示天气信息的元素document.getElementById('temperature').textContent = `温度: ${temperature}°C`;document.getElementById('humidity').textContent = `湿度: ${humidity}%`;}
}// 天气提醒观察者类
class WeatherAlertObserver extends Observer {update(weatherData) {const temperature = weatherData.temperature;const humidity = weatherData.humidity;// 根据天气情况进行提醒if (temperature > 30 && humidity > 70) {alert('天气炎热潮湿,注意防暑防潮!');}}
}// 使用示例
const weatherData = new WeatherData();
const displayObserver = new DisplayWeatherObserver();
const alertObserver = new WeatherAlertObserver();weatherData.addObserver(displayObserver);
weatherData.addObserver(alertObserver);// 模拟天气数据更新
weatherData.setWeatherData(28, 60);
weatherData.setWeatherData(32, 75);

        在上述示例中,WeatherData类作为主题,负责维护天气数据和观察者列表,并在数据更新时通知观察者。DisplayWeatherObserverWeatherAlertObserver类分别是不同的观察者,它们根据主题的通知进行相应的操作,一个更新页面显示,一个进行天气提醒

三、使用场景

1. 事件处理

        在前端页面中,用户的交互操作会触发各种事件,如按钮点击、表单提交等。可以将触发事件的元素看作主题,而处理这些事件的函数或模块看作观察者。例如,当用户点击一个登录按钮时,按钮是主题,登录验证逻辑、页面跳转逻辑等都是观察者,按钮点击后(主题状态改变),相应的观察者会执行各自的任务。

2. 数据绑定

        在现代前端框架中广泛应用的数据双向绑定机制通常基于观察者模式实现。当数据模型(主题)发生变化时,与之绑定的视图(观察者)会自动更新显示;反之,当用户在视图中进行操作修改数据时,数据模型也会相应更新,同时通知其他相关的视图进行更新。例如,在 Vue.js 中,当一个数据属性发生变化时,所有依赖该属性的模板部分都会自动重新渲染。

3. 异步通信

        当进行异步数据请求(如 AJAX 请求获取数据)时,请求对象可以看作主题,而请求成功后的后续处理逻辑(如更新页面数据、显示提示信息等)可以看作观察者。一旦数据请求完成(主题状态改变为获取到数据),观察者就会收到通知并进行相应的处理。

4. 组件通信

        在大型前端应用中,不同组件之间可能需要进行通信。例如,一个父组件可以作为主题,子组件作为观察者。当父组件的某些数据或状态发生变化时,它可以通知子组件进行相应的更新或操作,而子组件无需直接访问父组件的内部状态,保持了组件之间的独立性和解耦性。

四、优点

1. 解耦和灵活性

        主题和观察者之间的松散耦合使得它们可以独立变化和扩展。可以方便地添加新的观察者或修改现有观察者的行为,而不需要对主题的代码进行大量修改。同样,主题的内部实现可以改变,只要它仍然能够正确地通知观察者状态变化,观察者的代码也无需做太大调整。这使得系统更容易维护和扩展,适应不断变化的需求。

2. 实时响应和数据一致性

        观察者能够实时接收到主题状态变化的通知,并立即进行相应的更新操作,保证了系统中数据的一致性。用户在前端界面上看到的信息始终是最新的,提升了用户体验。例如,在数据绑定的场景中,用户对数据的修改可以立即反映在界面上,反之亦然。

3. 提高代码的可复用性

        观察者模式将不同的功能模块(观察者)分离出来,每个观察者只关注自己的特定任务和对主题状态变化的响应。这些观察者可以在不同的项目或场景中复用,只需要将它们与适当的主题进行关联即可。例如,一个用于显示数据变化的观察者组件可以在多个不同的数据展示页面中使用。

五、缺点

1. 内存泄漏风险

        如果在观察者不再需要关注主题状态变化时,没有正确地从主题的观察者列表中删除,那么即使观察者所关联的对象已经不再使用,它仍然会占用内存。这可能导致内存泄漏问题,尤其是在长时间运行的前端应用中。例如,当一个页面组件被销毁,但与之绑定的观察者没有被解除绑定,就会出现这种情况。

2. 通知顺序和复杂性

        当有多个观察者时,通知的顺序可能会影响系统的行为和结果。如果观察者之间存在依赖关系或者对通知顺序有要求,需要额外的机制来保证通知的正确顺序,这会增加系统的复杂性。而且,在复杂的系统中,可能难以追踪和理解所有观察者之间的交互关系以及它们对主题状态变化的响应逻辑。

3. 性能开销

        当主题频繁地发生状态变化并且有大量观察者时,通知所有观察者并执行它们的更新方法可能会带来一定的性能开销。特别是当观察者的更新操作比较复杂或者耗时较长时,可能会影响系统的整体性能和响应速度。例如,在一个实时数据更新的场景中,如果每秒都有大量数据更新并且有多个观察者进行复杂的计算和渲染操作,可能会导致页面卡顿。

六、注意事项

1. 正确管理观察者的生命周期

        在添加观察者时,要确保在合适的时机(通常是在观察者对象创建并准备好接收通知时)进行添加操作。同时,当观察者不再需要关注主题状态时,一定要及时从主题的观察者列表中删除,以避免内存泄漏。可以在观察者对象的销毁方法或者相关的生命周期钩子函数中执行删除操作。

2. 注意通知的频率和时机

        要合理控制主题状态变化的通知频率,避免过于频繁的通知导致性能问题。可以考虑对主题的状态变化进行适当的缓存或合并处理,在一定条件下再进行通知。同时,要确保通知的时机是合适的,即在主题的状态确实发生了有意义的变化并且观察者需要进行相应处理时才发送通知。

3. 处理好观察者之间的关系

        如果多个观察者之间存在依赖关系或者需要协同工作,要设计好它们之间的交互方式和顺序。可以通过引入中间协调者或者规定明确的通知顺序规则来解决。同时,要注意避免观察者之间的循环依赖等问题,以免导致系统出现异常行为或死锁。

4. 测试和调试

        由于观察者模式涉及多个对象之间的交互,在开发过程中要进行充分的测试和调试。可以编写单元测试来验证主题和观察者的功能是否正确,以及它们之间的交互是否符合预期。在调试时,可以利用浏览器的开发者工具等手段来跟踪观察对象的状态变化和通知过程,以便及时发现和解决问题。


http://www.ppmy.cn/embedded/133707.html

相关文章

【蓝桥杯选拔赛真题77】python计算小球 第十五届青少年组蓝桥杯python选拔赛真题 算法思维真题解析

目录 python计算小球 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python计算小球 第十五届蓝桥杯青少年组python比赛选拔赛真题 一、题目要…

计算机网络原理总结C-网络层

网络层 网络层提供的两种服务网际协议IP 虚拟互连网络IP地址子网掩码(无分类编址CIDR)IP地址和MAC地址IP数据报格式(路由)转发分组的流程 因特网的路由选择协议(动态路由协议) 网际控制报文协议ICMPIP多播…

手机无线/有线投屏电脑方案

我的电脑的设置-系统里没有“投影到这台电脑”选项,本文的方案是没有选项的情况下的下位替代,仅作记录 手机:oppo a11 电脑系统:win10 电脑应用:anlink,下载地址:AnLink安联 | AnLink 准备&…

如何学习人工智能?

学习人工智能(AI)是一个系统化的学习过程,需要从基础开始,逐步深入到高级技术。以下是一个从零开始到进阶的全面学习路线,帮助您掌握人工智能的核心概念和技术。 第一阶段:基础知识 1. 数学基础 线性代数…

LeetCode刷题日记之贪心算法(五)

目录 前言无重叠区间划分字母区间合并区间单调递增的数字监控二叉树总结 前言 随着对贪心算法的不断深入,本篇文章将继续挑战一些经典的题目,进一步巩固这一算法的应用技巧。希望博主记录的内容能够帮助大家更好地掌握贪心算法的解题思路✍✍✍ 无重叠区…

扶贫工作数字化:SpringBoot精准扶贫系统

2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…

尚硅谷-react教程-求和案例-@redux-devtools/extension 开发者工具使用-笔记

## 7.求和案例_react-redux开发者工具的使用(1).npm install redux-devtools/extension(2).store中进行配置import { composeWithDevTools } from redux-devtools/extension;export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk))) src/redux/s…

PHP+REDIS设置请求限流(设置1秒内最大请求数1000QPS)

双十一期间要做活动,设置商品请求限流,护航秒杀活动正常进行!#设置1秒内最多同时1000请求 $maxNum 1000; $redisKey GoldMall:Huodong:gid.$g_id._.date(s); $onlineNum (int)$this->redis->get($redisKey); if($onlineNum){$online…