观察者模式的思考

devtools/2024/10/20 15:40:54/

观察者模式由来

观察者模式(Observer Pattern)是一种行为型设计模式,它的起源可以追溯到20世纪90年代初,由设计模式四人帮(Erich Gamma, Richard Helm, Ralph Johnson 和 John Vlissides)在其著作《设计模式:可复用面向对象软件的基础》中首次提出。观察者模式用于解决对象之间的一对多依赖关系,当一个对象(被观察者)的状态发生改变时,所有依赖于它的对象(观察者)都会得到通知并自动更新。

概念

  1. 被观察者(Subject):定义一个接口,用于添加、删除和通知观察者。
  2. 观察者(Observer):定义一个接口,用于接收被观察者的通知并执行相应的操作。
  3. 具体被观察者(ConcreteSubject):实现被观察者接口,维护观察者列表,并在状态改变时通知所有观察者。
  4. 具体观察者(ConcreteObserver):实现观察者接口,具体实现接收到通知后的操作。

请在此添加图片描述

实现原理

观察者模式的核心原理是通过将对象间的依赖关系从硬编码转移到外部,使得一个对象(被观察者)可以在不通知其他对象的情况下更改其状态,然后在适当的时候通知所有依赖于它的对象(观察者)。这种解耦的设计方式使得代码更加灵活,易于扩展和维护。

我有一个朋友张三,他总是关心天气情况,每天会看天气预报,在这个过程中,天气预报(被观察者)和张三(观察者)之间就会存在一种依赖关系。当天气预报发生变化时,张三需要得到通知并及时更新自己的信息。

定义角色

  • 被观察者(Subject):天气预报。它包含了当前的天气状况以及未来一段时间内的天气预报信息。
  • 观察者(Observer):张三。他是一个依赖于天气预报信息的用户。

建立依赖关系

  • 张三订阅了天气预报服务,这样当他打开电视或查看手机时,就能接收到最新的天气预报信息。

事件通知机制

  • 天气预报服务会在天气状况发生变化时,或者新的预报信息生成时,触发通知机制。这个机制负责将最新的天气信息发送给所有订阅了服务的用户,包括张三。

更新策略

  • 张三在接收到天气预报信息后,会根据信息的内容更新自己的认知,比如决定是否要带伞、穿什么衣服等。

动态加入和退出

  • 如果张三决定不再订阅天气预报服务,他可以随时取消订阅。同样,如果张三从一个城市搬到另一个城市,他可以订阅新的城市的天气预报服务。

技术实现

首先,我们定义一个Subject接口和一个Observer接口:

javascript">// 被观察者
public interface Subject {void registerObserver(Observer observer);void removeObserver(Observer observer);void notifyObservers();
}// 观察者
public interface Observer {void update(String message);
}

然后,我们创建一个WeatherForecast类作为被观察者,实现Subject接口:

javascript">import java.util.ArrayList;
import java.util.List;public class WeatherForecast implements Subject {private List<Observer> observers = new ArrayList<>();private String message;public void setMessage(String message) {this.message = message;notifyObservers();}@Overridepublic void registerObserver(Observer observer) {observers.add(observer);}@Overridepublic void removeObserver(Observer observer) {observers.remove(observer);}@Overridepublic void notifyObservers() {for (Observer observer : observers) {observer.update(message);}}
}

接下来,我们创建一个WeatherWatcher类作为观察者,实现Observer接口:

javascript">public class WeatherWatcher implements Observer {private String name;public WeatherWatcher(String name) {this.name = name;}@Overridepublic void update(String message) {System.out.println(name + " received weather forecast: " + message);}
}

最后,我们在主函数中创建一个WeatherForecast对象和两个WeatherWatcher对象,并让它们订阅天气预报:

javascript">public static void main(String[] args) {WeatherForecast weatherForecast = new WeatherForecast();WeatherWatcher watcher1 = new WeatherWatcher("张三");WeatherWatcher watcher2 = new WeatherWatcher("李四");weatherForecast.registerObserver(watcher1);weatherForecast.registerObserver(watcher2);weatherForecast.setMessage("今天天气晴朗,温度适中。");weatherForecast.setMessage("明天将会有大雨,请携带雨具。");
}

运行这个程序,你会看到张三和李四都收到了天气预报的通知。

请在此添加图片描述

Spring 实现

定义事件类:首先,我们需要定义一个事件类,它将携带被观察者状态变化的信息。

javascript">package com.neo.design.observer;import org.springframework.context.ApplicationEvent;public class WeatherEvent extends ApplicationEvent {private String weatherInfo;public WeatherEvent(Object source, String weatherInfo) {super(source);this.weatherInfo = weatherInfo;}public String getWeatherInfo() {return weatherInfo;}
}
  1. 创建事件发布者:接下来,我们创建一个事件发布者,它将负责发布天气变更事件。在这个例子中,我们将使用Spring的ApplicationEventPublisher来发布事件。
javascript">import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;@Component
public class WeatherEventPublisher {private final ApplicationEventPublisher publisher;public WeatherEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}public void publishWeatherChangeEvent(String message) {publisher.publishEvent(new WeatherChangeEvent(message));}
}

创建事件监听器:然后,我们创建一个事件监听器,它将实现ApplicationListener接口,并重写onApplicationEvent方法。在这个方法中,我们将处理天气变更事件,并通知相关的观察者。

javascript">package com.neo.design.observer;import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;@Component
public class WeatherEventPublisher {private final ApplicationEventPublisher publisher;public WeatherEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}public void publishWeatherChangeEvent(String message) {publisher.publishEvent(new WeatherChangeEvent(message));}
}

创建用户服务:我们还需要创建一个用户服务,它将负责管理用户的订阅信息,并在接收到天气变更事件时通知用户。

javascript">package com.neo.design.observer;import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Service
public class UserService {private final List<String> subscribers = new ArrayList<>();public void subscribe(String subscriber) {subscribers.add(subscriber);}public void notifySubscribers(String message) {for (String subscriber : subscribers) {System.out.println(subscriber + " received weather forecast: " + message);}}
}

创建控制器:最后,我们创建一个控制器,它将接收用户订阅请求和天气变更请求,并调用相应的服务来处理这些请求。

javascript">package com.neo.design.observer;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@RestController
public class WeatherForecastController {@Autowiredprivate UserService userService;@Autowiredprivate WeatherEventPublisher publisher;@PostMapping("/subscribe")public String subscribe(@RequestParam("subscriber") String subscriber) {userService.subscribe(subscriber);return "Subscriber added!";}@PostMapping("/update-weather")public String updateWeather(@RequestParam("message") String message) {publisher.publishWeatherChangeEvent(message);return "Weather updated!";}
}

通过以上设计,我们利用Spring Boot的事件机制和依赖注入特性实现了一个高效的观察者模式

验证

新增一名观察者

请在此添加图片描述

设定一个被观察者所关注的消息。

请在此添加图片描述

执行功能,返回测试结果如下

请在此添加图片描述

总结

观察者模式(Observer Pattern)在软件工程设计中扮演着重要角色,观察者模式实现了发布者(主题)和订阅者(观察者)之间的松散耦合。发布者无需知道具体的订阅者是谁,只需要维护一个订阅者列表,并在状态变化时通知它们。这种解耦使得系统更具灵活性和可扩展性。通过观察者模式,添加或移除订阅者非常容易,不需要修改发布者的代码。只需实现观察者接口并注册或取消注册即可。这使得系统在需求变化或扩展时更易于维护。它适用于各种需要实时更新和异步处理的场景,提升了系统的响应能力和用户体验,是设计模式中一个非常实用且常用的模式。


http://www.ppmy.cn/devtools/127320.html

相关文章

rel-例行性工作

1&#xff0c;at命令 /etc/at.allow&#xff0c;写在该文件的人可以使用at命令 /etc/at.deny&#xff0c;黑名单 两个文件如果都不存在&#xff0c;只有root能使用 使用方法 at 命令格式&#xff1a; at [参数] [时间] 实例 建立一个3分钟后给所有用户发送 hahaha 2&#x…

Vue.js 学习总结(10)—— Vue 前端项目性能优化常用技巧

1. 使用路由懒加载 在 Vue.js 应用中&#xff0c;路由懒加载可以延迟加载路由组件直到它们被需要时才加载&#xff0c;从而减少应用的初始加载时间。示例代码&#xff1a; // router/index.js import { createRouter, createWebHistory } from vue-router;const Home () >…

AIGC技术的学习 系列二

文章目录 前言一、AIGC是什么?1.1. 基本概念1.2机器学习分类二、 语言模型2.1. 基于统计的语言模型。2.2. 基于神经网络的语言模型。2.3. 基于预训练机制的的语言模型/大语言模型三、读入数据3.1. 不得不说的Transformer3.2. 影响力3.3. 根据人类反馈的强化学习3.4. 生成式AI3…

flex布局

1、在 flex 容器上&#xff0c;设置align-items , 子项就会保持其自身的高度了 &#xff0c;不会自动高度对齐。 2、 <div class"flexboxborder"><div class"flexbox3"><div class"flexitem flexbox1"><div class"f…

探索社交网络中的情感脉动 | 微博评论舆情分析系统

在大数据时代&#xff0c;信息量爆炸&#xff0c;我们该如何快速理解那些海量的舆情&#xff1f;尤其是在像微博这样的平台上&#xff0c;评论区更是舆论风向的“前哨站”。今天给大家带来的这个微博评论舆情分析系统&#xff0c;就是一款为大家解决这一痛点的工具&#x1f50d…

docker 基础镜像里 scratch 和alpine,ubuntu centos详细对比(镜像优化)

1. scratch 特点 极简&#xff1a;scratch 是一个空的镜像&#xff0c;没有任何操作系统或文件系统。 体积&#xff1a;scratch 镜像的大小几乎为零&#xff0c;是最小的镜像。 灵活性&#xff1a;完全由用户自定义&#xff0c;没有任何预装的工具或库。 依赖管理&#xff1…

Flink时间窗口程序骨架结构

前言 Flink 作业的基本骨架结构包含三部分&#xff1a;创建执行环境、定义数据处理逻辑、提交并执行Flink作业。 日常大部分 Flink 作业是基于时间窗口计算模型的&#xff0c;同样的&#xff0c;开发一个Flink时间窗口作业也有一套基本的骨架结构&#xff0c;了解这套结构有助…

车载软件架构---软件定义汽车的复杂性

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…