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

设计模式专栏: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 镜像…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
1688商品列表API与其他数据源的对接思路
将1688商品列表API与其他数据源对接时,需结合业务场景设计数据流转链路,重点关注数据格式兼容性、接口调用频率控制及数据一致性维护。以下是具体对接思路及关键技术点: 一、核心对接场景与目标 商品数据同步 场景:将1688商品信息…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
【单片机期末】单片机系统设计
主要内容:系统状态机,系统时基,系统需求分析,系统构建,系统状态流图 一、题目要求 二、绘制系统状态流图 题目:根据上述描述绘制系统状态流图,注明状态转移条件及方向。 三、利用定时器产生时…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解
JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用,结合SQLite数据库实现联系人管理功能,并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能,同时可以最小化到系统…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...
