探索设计模式的魅力:捕捉变化的风-用观察者模式提升用户体验

设计模式专栏:http://t.csdnimg.cn/U54zu
目录
一、引言
核心概念
应用场景
可以解决的问题
二、场景案例
2.1 不用设计模式实现
2.2 存在问题
2.3 使用设计模式实现
2.4 成功克服
三、工作原理
3.1 结构图和说明
3.2 工作原理详解
3.3 实现步骤
四、 优势
4.1 好处和优势
4.2 应用示例
4.3 系统性优势
五、局限性和注意事项
5.1 局限性与不适用的场景
5.2 实际应用中的注意事项与建议
一、引言
核心概念
| 优雅方案
观察者模式的核心概念主要包含以下几个关键部分: 1. 主题(Subject):
2. 观察者(Observer):
3. 具体主题(Concrete Subject):
4. 具体观察者(Concrete Observer):
5. 状态(State):
6. 更新接口(Update Method):
|
简单而言
| 观察者模式允许对象之间建立一种一对多的依赖关系,当一个对象改变状态时,所有依赖它的对象都会得到通知并且自动更新。这种通知机制使得对象之间的通信更加松散耦合、灵活可扩展。无需直接相互引用,各个对象能够独立变化而互不影响。通过观察者模式,我们能够轻松实现实时更新、动态同步等功能,从而为我们的应用带来更好的用户体验和可维护性。无论是构建响应式界面、事件驱动系统,还是实现即时通信、发布-订阅模式,观察者模式都是一个不可或缺的设计选择。 |
接下来,让我们深入探索观察者模式的内部工作原理和实际应用案例,享受软件开发的乐趣吧!
应用场景
观察者模式在软件设计中的应用场景
| 1. 用户界面(UI)交互:
2. 事件监测系统:
3. 发布/订阅系统:
4. 数据模型与视图同步:
|
可以解决的问题
| 在现代软件开发中,组件间的交互和状态同步是一项常见而又至关重要的挑战。随着业务逻辑的复杂化及用户需求的不断变化,如何设计出灵活、低耦合的系统成为软件工程师面临的一大课题。对于系统中存在的一个实体状态改变需要通知到一个或多个依赖该状态的实体的场景,如何高效率地处理这种状态同步与通知呢?传统的紧耦合联系很难应对系统的迅速变化和扩展,而观察者模式在此场景下应运而生,提供了一种优雅且实用的设计解决方案。 |
使用观察者模式可以解决的问题包括但不限于
|
二、场景案例
经典场景:新闻发布系统
| 最经典的观察者模式场景之一是“新闻发布系统”。在这个场景中,有多个订阅者(观察者)对新闻感兴趣,他们希望在有新闻发布时能够立即得到通知。系统管理员(主题)负责发布新闻。 |
2.1 不用设计模式实现
一坨坨代码实现
import java.util.ArrayList;
import java.util.List; // 新闻类
class News { private String content; public News(String content) { this.content = content; } public String getContent() { return content; }
} // 新闻发布器
class NewsPublisher { private List<NewsSubscriber> subscribers = new ArrayList<>(); // 订阅新闻 public void subscribe(NewsSubscriber subscriber) { subscribers.add(subscriber); } // 取消订阅 public void unsubscribe(NewsSubscriber subscriber) { subscribers.remove(subscriber); } // 发布新闻 public void publish(News news) { for (NewsSubscriber subscriber : subscribers) { subscriber.receiveNews(news); } }
} // 新闻订阅者接口
interface NewsSubscriber { void receiveNews(News news);
} // 网页新闻订阅者
class WebNewsSubscriber implements NewsSubscriber { @Override public void receiveNews(News news) { System.out.println("WebNewsSubscriber: News received - " + news.getContent()); }
} // 邮件新闻订阅者
class EmailNewsSubscriber implements NewsSubscriber { @Override public void receiveNews(News news) { System.out.println("EmailNewsSubscriber: News received - " + news.getContent()); }
} // 客户端代码
public class NewsSystemClient { public static void main(String[] args) { NewsPublisher publisher = new NewsPublisher(); NewsSubscriber webSubscriber = new WebNewsSubscriber(); NewsSubscriber emailSubscriber = new EmailNewsSubscriber(); publisher.subscribe(webSubscriber); publisher.subscribe(emailSubscriber); News news = new News("Breaking News: World Peace Achieved!"); publisher.publish(news); }
}
在这个实现中,NewsPublisher 类充当了新闻发布的中心角色,它维护了一个订阅者列表。当有新闻发布时,NewsPublisher 会遍历订阅者列表,并调用每个订阅者的 receiveNews 方法来传递新闻。
2.2 存在问题
| 上述不使用设计模式实现的新闻发布系统确实存在一些问题,尽管它能够实现基本的新闻发布和接收功能。以下还是存在如紧耦合、缺乏灵活性、可扩展性差、错误处理不足、缺乏抽象层次 和 动态性受限 等问题。 |
2.3 使用设计模式实现
1. 观察者接口(Observer):定义一个更新方法,当新闻发布时,所有观察者都将调用此方法。
public interface Observer { void update(String news);
}
2. 具体观察者(ConcreteObserver):实现观察者接口,当有新闻发布时,执行具体的操作。例如,将新闻显示在网页上、发送到用户的电子邮箱等。
public class WebObserver implements Observer { @Override public void update(String news) { System.out.println("WebObserver: Display news on website - " + news); }
} public class EmailObserver implements Observer { @Override public void update(String news) { System.out.println("EmailObserver: Send news to email - " + news); }
}
3. 主题接口(Subject):定义一个注册观察者、移除观察者和通知观察者的方法。
import java.util.ArrayList;
import java.util.List; public interface Subject { void registerObserver(Observer observer); void removeObserver(Observer observer); void notifyObservers(String news);
}
4. 具体主题(ConcreteSubject):实现主题接口,维护一个观察者列表,并在有新闻发布时通知所有观察者。
public class NewsSubject implements Subject { private List<Observer> observers = new ArrayList<>(); @Override public void registerObserver(Observer observer) { observers.add(observer); } @Override public void removeObserver(Observer observer) { observers.remove(observer); } @Override public void notifyObservers(String news) { for (Observer observer : observers) { observer.update(news); } }
}
5. 客户端代码(Client):创建主题和观察者对象,并将观察者注册到主题上。当有新闻发布时,主题会通知所有观察者。
public class Client { public static void main(String[] args) { NewsSubject newsSubject = new NewsSubject(); Observer webObserver = new WebObserver(); Observer emailObserver = new EmailObserver(); newsSubject.registerObserver(webObserver); newsSubject.registerObserver(emailObserver); newsSubject.notifyObservers("Breaking News: World Peace Achieved!"); }
}
在这个场景中,新闻发布系统(主题)负责发布新闻,而网页观察者(WebObserver)和电子邮件观察者(EmailObserver)则负责在新闻发布时显示和发送新闻。通过使用观察者模式,系统管理员可以轻松地添加或移除观察者,而无需修改主题代码。此外,当有新闻发布时,所有观察者都会自动收到通知并更新,从而实现了松耦合和可扩展性。
2.4 成功克服
使用观察者模式在上述示例中成功克服了多个问题,这些问题在使用直接的方法调用和对象间显式交互时可能会出现。以下是观察者模式成功克服的问题:
| 1. 紧耦合:
2. 缺乏灵活性:
3. 可扩展性差:
4. 错误处理不足:
5. 缺乏抽象层次:
6. 动态性受限:
|
三、工作原理
3.1 结构图和说明

- Subject:目标对象,通常具有如下功能。
- Observer:定义观察者的接又,提供目标通知时对应的更新方法,这个更新方法进行相应的业务处理,可以在这个方法里面回调目标对象,以获取目标对象的数据。
-
ConcreteSubject:具体的目标实现对象,用来维护目标状态,当目标对象的状态发生改变时,通知所有注册的、有效的观察者,让观察者执行相应的处理
-
ConcreteObserver:观察者的具体实现对象,用来接收目标的通知,并进行相应的后续处理,比如更新自身的状态以保持和目标的相应状态一致。
3.2 工作原理详解
| 1. 注册与订阅:
2. 状态变化:
3. 通知更新:
4. 解耦:
5. 灵活性和扩展性:
|
3.3 实现步骤
使用观察者模式实现功能时,你可以按照以下思考步骤进行:
| 1. 定义主题和观察者之间的关系:
2. 设计通用接口:
3. 实现注册与移除观察者功能:
4. 定义通知机制:
5. 更新观察者状态:
6. 考虑线程安全与性能问题:
7. 实现解耦:
8. 处理异常与错误:
9. 测试:
10. 细节优化:
|
在应用观察者模式时,始终需要关注设计的整体清晰度、灵活性以及扩展性,确保最终实现的模式适合应用的上下文环境。
四、 优势
4.1 好处和优势
使用观察者模式可以带来以下明显的好处和优势:
| 1. 解耦:
2. 灵活性:
3. 动态响应:
4. 简化通信:
|
4.2 应用示例
| 考虑一个电商平台上的商品定价系统。传统的设计方法可能会要求每个依赖商品价格信息的组件都直接从价格数据库中获取更新。随着系统的发展,这种紧耦合的方式导致了若干问题:每当价格更新逻辑变化时,所有依赖组件都需要作出相应修改;系统的可扩展性差,添加新的依赖组件会带来额外的维护负担。 |
| 采用观察者模式改进后,价格系统作为主题,各个依赖组件(如库存管理、促销引擎、前端显示等)作为观察者。当商品价格更新时,价格系统仅需通知这些观察者。这样,库存管理系统可以自动调整库存采购策略,促销引擎可以同步更新促销活动,用户界面也可以即时显示最新价格。这种方式不仅使得价格更新流程更加清晰,而且让各组件能够更加独立地开发和维护。 |
4.3 系统性优势
观察者模式通过解耦观察者和被观察者之间的关系,提高了系统的灵活性、扩展性和可维护性。具体来说:
| 1. 灵活性:
2. 扩展性:
3. 可维护性:
|
综上所述,观察者模式通过解耦观察者和被观察者之间的关系,提高了系统的灵活性、扩展性和可维护性。它简化了对象之间的通信和协作,使得代码更加清晰、简洁和易于维护。因此,在现代软件开发中,观察者模式被广泛应用于处理不同组件之间的高效通信和动态响应问题。
五、局限性和注意事项
5.1 局限性与不适用的场景
尽管观察者模式为软件开发带来了许多好处,但在某些情况下,它也可能存在局限性和不适用的场景:
| 1. 复杂的依赖关系:
2. 性能考虑:
3. 循环依赖:
4. 错误处理:
|
5.2 实际应用中的注意事项与建议
| 1. 推送 vs 拉取:
2. 通知顺序:
3. 通知效率:
4. 循环依赖:
5. 内存管理:
6. 异常处理:
7. 状态一致性:
8. 设计模式的组合使用:
|
在遵守这些注意事项和建议的同时,应该记住设计模式不是万能的,不应该强行适配模式。在选择应用观察者模式前,确保它适合当前的问题场景,并充分考虑它可能带来的设计复杂性。
相关文章:
探索设计模式的魅力:捕捉变化的风-用观察者模式提升用户体验
设计模式专栏:http://t.csdnimg.cn/U54zu 目录 一、引言 核心概念 应用场景 可以解决的问题 二、场景案例 2.1 不用设计模式实现 2.2 存在问题 2.3 使用设计模式实现 2.4 成功克服 三、工作原理 3.1 结构图和说明 3.2 工作原理详解 3.3 实现步骤 四、 优…...
SpringCloud-高级篇(十九)
我们已经学过使用 SpringAMQP去收和发消息,但是发和收消息是只是MQ最基本的功能了,在收发消息的过程中,会有很多的问题需要去解决,下面需要学习rabbitMQ的高级特性去解决 死信交换机:这个可以帮助我们实现消息的延迟的…...
Junit常用断言
0.断言简介 断言:assert Q:断言的作用 更方便的对结果进行判定 "有针对性"的if判断 针对两个变量值是否相同 使用assertEquals针对两个对象是否相同 使用assertSame针对返回值是否为True 使用assertTrue 1.断言的参数 assertXXX(”断言失败时提升的信息“&#x…...
docker 实现 mysql:8.3.0 主从复制(2024年2月13日最新版本)
环境为 CentOS 7.6, 具体操作请看MySQL主从复制01-主从复制概述及原理_哔哩哔哩_bilibili 1、配置主服务器 # 启动主服务器 docker run -p 3306:3306 --name mysql_master -e MYSQL_ROOT_PASSWORDnmnmnm67890890 -v /docker/mysql_master/conf:/etc/mysql/conf.d…...
STM32 + ESP8266,连接阿里云 上报/订阅数据
(文章正在编辑中,一点点地截图操作过程,估计要拖拉两三天) 一、烧录MQTT固件 ESP8266出厂时,默认是AT固件。连接阿里云,需要使用MQTT固件。 1、独立EPS8266模块的烧录方法 2、魔女开发板,板载…...
如何利用chatgpt提升工作效率?
在数字化和信息化的时代,人工智能技术已经深入到了我们生活的方方面面。其中,ChatGPT作为当前热门的人工智能技术,以其强大的自然语言处理能力和广泛的应用场景,正逐渐改变着我们的工作方式,为我们提高工作效率提供了全…...
MongoDB聚合:$geoNear
$geoNear根据指定的点按照距离以由近到远的顺序输出文档。 从4.2版本开始,MongoDB移除了limit和num选项以及100个文档的限制,如果要限制结果文档的数量可以使用$limit阶段。 语法 { $geoNear: { <geoNear options> } }$geoNear操作接受一个包含…...
Docker-CE 国内源国内镜像
Docker-CE 就是 Docker Community Edition 的意思 docker-ce由docker官方维护 , docker.io由Debian维护 Docker官文 – Install Docker Engine on CentOS Docker官文 – Install Docker Engine on Fedora Docker官文 – Install Docker Engine on Debian Docker官文 – In…...
【Tauri】(3):使用Tauri1.5版本,进行桌面应用开发,在windows上搭建环境,安装node,rust环境,可以打包成功,使用vite创建应用
1,视频地址: https://www.bilibili.com/video/BV1Ny421a7nA/ 【Tauri】(3):使用Tauri1.5版本,进行桌面应用开发,在windows上搭建环境,安装node,rust环境,可以…...
C++ 堆排序
C 堆排序 堆排序是一种基于二叉堆数据结构的排序算法,其原理如下: 构建最大堆:将待排序的数组看作一个完全二叉树,并通过调整节点的位置构建一个最大堆。最大堆满足每个父节点的值都大于或等于其子节点的值。构建最大堆的过程可以…...
U3D记录之FBX纹理丢失问题
今天费老大劲从blender建了个模型,然后导出进去unity 发现贴图丢失 上网查了一下 首先blender导出要改设置 这个path mode要copy 然后unity加载纹理也要改设置 这里这个模型的纹理load要改成external那个模式 然后就有了,另外这个导出还有好多选项可…...
监测Nginx访问日志502情况后并做相应动作
今天带大家写一个比较实用的脚本哈 原理: 假设服务器环境为lnmp,近期访问经常出现502现象,且502错误在重启php-fpm服务后消失,因此需要编写监控脚本,一旦出现502,则自动重启php-fpm服务 场景: 1…...
【数据分享】1929-2023年全球站点的逐年平均风速(Shp\Excel\免费获取)
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、能见度等指标,说到气象数据,最详细的气象数据是具体到气象监测站点的数据! 有关气象指标的监测站点数据,之前我们分享过1929-2023年全球气象站…...
Android性能调优 - 应用安全问题
Android应用安全 1.组件暴露: 像比如ContentProvider,BroadcastReceiver,Activity等组件有android:exported属性; 如果是私有组件 android:exported “false”; 如果是公有组件 android:exported “true” 且进行权限控制&…...
C#的Char 结构的像IsLetterOrDigit(Char)等常见的方法
目录 一、Char 结构的方法 二、Char.IsLetterOrDigit 方法 1.Char.IsLetterOrDigit(Char)用法 2.IsLetterOrDigit(String, Int32)方法 三、Char.IsLetter 方法 1.IsLetter(Char) 2.IsLetter(String, Int32) 四、Char.IsDigit 方法 1. IsDigit(String, Int32) 2.IsDig…...
部分意图分类【LLM+RAG】
在生成人工智能领域工作最有价值的事情之一就是发现新兴技术如何融入新的解决方案。 举个例子:在为北美顶级金融服务公司之一设计对话式人工智能助手时,WillowTree 的数据和人工智能研究团队 (DART) 发现,将意图分类与大型语言模型 (LLM) 结合…...
1277. 统计全为 1 的正方形子矩阵
1277. 统计全为 1 的正方形子矩阵 题目链接:1277. 统计全为 1 的正方形子矩阵 代码如下: class Solution { public:int countSquares(vector<vector<int>>& matrix) {if(matrix.size()0||matrix[0].size()0) return 0;//dp[i][j]代表…...
Python 3 时间序列可视化指南
简介 时间序列分析属于统计学的一个分支,涉及对有序的、通常是时间性的数据进行研究。当适当应用时,时间序列分析可以揭示意想不到的趋势,提取有用的统计数据,甚至预测未来的趋势。因此,它被应用于许多领域࿰…...
[算法前沿]--059-大语言模型Fine-tuning踩坑经验之谈
前言 由于 ChatGPT 和 GPT4 兴起,如何让人人都用上这种大模型,是目前 AI 领域最活跃的事情。当下开源的 LLM(Large language model)非常多,可谓是百模大战。面对诸多开源本地模型,根据自己的需求,选择适合自己的基座模型和参数量很重要。选择完后需要对训练数据进行预处…...
【Docker】01 Docker安装与配置
文章目录 一、Docker二、离线安装Docker三、联网安装Docker3.1 下载YUM软件库文件3.2 安装epel-release3.3 安装yum-utils3.4 设置镜像仓库3.5 查看docker-ce所有版本3.6 安装Docker3.7 启动Docker3.8 查看Docker信息3.9 启动第一个容器 四、一些配置4.1 登录DockerHub4.2 镜像…...
微积分入门书籍之高考篇
导数的秘密(第二版)-2021.01 高考导数满分精讲(2021) 高考导数探秘:解题技巧与策略 董晟渤(2024.10) 微积分与高考数学(第2版)-2024 高考导数解题全攻略(2024…...
Spring Validation嵌套校验踩坑实录:用@Valid搞定订单里商品列表的深度验证
Spring Validation嵌套校验实战:用Valid解决订单商品列表的深度验证难题 电商系统中订单创建接口的复杂性往往体现在数据结构的嵌套层级上。一个典型的订单对象不仅包含基础订单信息,还会内嵌商品列表、优惠券、收货地址等多个子对象。当后端接收到这样的…...
标签系统的底层同步拓扑:大批量客户标签异步更新的一致性方案
标签(Tag)是私域精细化运营的灵魂。在进行大规模广告投放、或者老客清洗时,企业系统经常需要同时为上万个外部客户批量追加或清空标签。 1. 标签同步的复杂性在哪里? 原生设计中,企业微信的标签是以“企业标签组&#…...
Claude Code 用户如何通过 Taotoken 配置稳定 API 连接避免封号困扰
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Claude Code 用户如何通过 Taotoken 配置稳定 API 连接避免封号困扰 基础教程类,针对经常遇到 Claude Code 封号或 Tok…...
月度补丁如何落地?Claude Code 在商业项目中实现版本追新的 4 步更新机制
1. 月度补丁不是“一键升级”,而是四次有节奏的上下文重校准 大多数人把 Claude Code 的月度补丁理解成“换了个模型版本号”——就像给手机系统点一下“更新”。我去年在三个中型商业项目里连续踩了这个坑:每次新补丁发布后,团队反馈“AI 写的代码变奇怪了”,review 通过…...
C盘告急?手把手教你用mklink命令把Fusion 360挪到D盘(Win11保姆级教程)
拯救C盘空间:用符号链接将Fusion 360迁移到D盘的完整指南 当C盘空间告急时,很多用户会发现Fusion 360默认安装在系统盘,占用了大量宝贵空间。本文将详细介绍如何利用Windows的mklink命令,在不影响软件功能的前提下,将F…...
别再死磕GAN了!用PyTorch从零实现DDPM扩散模型,手把手带你跑通CIFAR-10生成
从GAN到DDPM:用PyTorch实战扩散模型的图像生成革命 当我在2022年第一次看到DALLE 2生成的超现实图像时,作为一名长期使用GAN的开发者,我意识到生成式AI正在经历一场静默的革命。传统GAN虽然能生成惊艳的结果,但其训练过程就像在钢…...
Maintain Certificate Trust List,把 SAP 出站通信里的证书信任关口管清楚
做 SAP S/4HANA Cloud、SAP BTP ABAP environment 或者混合架构里的出站集成时,有一个问题很容易被业务侧低估,却经常成为接口上线前的最后一道卡点,SAP 系统到底信不信任通信伙伴的服务器证书。OAuth、Basic Authentication、Communication Arrangement、Destination、ODat…...
碳化硅肖特基二极管B1D06065KS在PFC电路中的高效应用与设计要点
1. 项目概述:从一颗二极管到高效能电源的心脏最近在做一个服务器电源的优化项目,客户对效率和功率密度要求近乎苛刻。传统的硅基器件在高压、高频下的损耗和温升成了瓶颈,团队讨论后决定在关键的前级功率因数校正(PFC)…...
昇思大模型垂域模型
昇思 MindSpore 垂域模型是基于通用大模型基座 行业数据微调 领域技术增强构建的行业专用 AI 模型,依托 MindSpore Transformers 套件与昇腾硬件,在医疗、金融、电力、法律、工业等领域实现深度落地,兼顾通用能力与行业专业性,训…...
