【观察者设计模式详解】C/Java/JS/Go/Python/TS不同语言实现

简介

观察者模式(Observer Pattern)是一种行为型模式。它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

观察者模式使用三个类Subject、Observer和Client。Subject对象带有绑定观察者到Client对象和从Client对象解绑观察者的方法。我们创建Subject类、Observer抽象类和扩展了抽象类Observer的实体类。

作用

  1. 一个对象状态更新,其他依赖对象收到通知和自动更新的机制。
  2. 实现模块化分离,实现主题与观察者交互对象之间的松耦合。
    1)观察者定义了对象之间一对多的关系。
    2)被观察者(主题)用一个共同的接口来更新观察者。
    3)观察者和被观察者用松耦合方式结合,被观察者不知道观察者的细节,只知道观察者实现了观察者接口。

实现步骤

  1. 创建观察者observer基础接口,包含主题和更新方法
  2. 创建主题subject抽象类,包含observer列表以及添加和删除方法
  3. 创建具体的主题类,实现通知方法,发布通知时轮询通知全部观察者
  4. 创建多个具体观察者,与主题关联,并实现自己的更新方法
  5. 客户调用时先声明主题,再将观察者分别添加到主题,当主题发布通知时,观察者自动更新

UML

 

Java代码

观察者接口

 
// ObserverAPI.java 观察者接口,Java 9已经默认支持Observer接口
// 这里避免冲突采取ObserverAPI命名
public interface ObserverAPI {
public Subject subject = null;
public void update(String content);
}

具体观察者

 
// ConcreteObserver.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
// 不同的观察者也可以对应多个主题
public class ConcreteObserver implements ObserverAPI {
public Subject subject;
// 给观察者绑定主题,同时把观察者添加到主题列表
public ConcreteObserver(Subject subject) {
this.subject = subject;
this.subject.register((ObserverAPI) this);
}
// 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听
public void update(String content) {
System.out.println(String.format("%s::update() [subject.name = %s content = %s]",
this.getClass().getName(),
this.subject.getName(), content));
}
}

 
// ConcreteObserver2.java 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
// 不同的观察者可以对应不同的主题。
public class ConcreteObserver2 implements ObserverAPI {
// 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者
public ConcreteObserver2() {
}
// 观察者发出更新通知,观察者自行监听
public void update(String content) {
System.out.println(String.format("%s::update() [content = %s]",
this.getClass().getName(), content));
}
}

抽象主题类

 
// Subject.java 定义抽象主题类或者接口,供具体主题类继承
public abstract class Subject {
private String name;
// protected Set<ObserverAPI> observers = new HashSet<>();
protected List<ObserverAPI> observers = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void register(ObserverAPI observer) {
System.out.println(this.getClass().getName() + "::register() [observer = " + observer.getClass().getSimpleName() + "]");
observers.add(observer);
}
public void remove(ObserverAPI observer) {
observers.remove(observer);
}
// 通知由具体类来实现逻辑
public abstract void notify(String name);
}

具体主题类

 
// ConcreteSubject.java 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。
public class ConcreteSubject extends Subject {
public ConcreteSubject(String name) {
this.setName(name);
}
// 不同的主题类有自己的通知方法,批量通知绑定的观察者
@Override
public void notify(String content) {
System.out.println(this.getClass().getName() + "::notify() [content = " + content + "]");
for (Object observer : this.observers) {
((ObserverAPI) observer).update(content);
}
}
}

测试调用

 
/**
* 观察者模式应用非常广泛,主要是观察者提前绑定到发布者
* 当发布者发布消息时,批量广播通知,而无需逐一通知
* 观察者监听到消息后自己决定采取哪一种行为
*/
// 定义一个主题,也就是发布者
Subject concreteSubject = new ConcreteSubject("subject1");
// 再声明观察者,通过构造器注册到主题上
ObserverAPI observer1 = new ConcreteObserver(concreteSubject);
// 也可以单独给主题注册一个新的观察者
concreteSubject.register(new ConcreteObserver2());
// 可以移除观察者对象,可以打开注释试下
// concreteSubject.remove(observer1);
// 主题开始发布新通知,各观察者自动更新
concreteSubject.notify("hello, this is broadcast.");

Python代码

观察者接口

 
# ObserverAPI.py 观察者抽象父类,定义一些公共方法
class ObserverAPI:
def __init__(self, name):
self.name = name
# 观察者发出更新通知,观察者自行监听
def update(self, content):
print(self.__class__.__name__ + '::update() [content = ' + content + ']')
def set_name(self, name):
self.name = name

具体观察者

 
# ConcreteObserver.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者也可以对应多个主题
from src.ObserverAPI import ObserverAPI
# 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者也可以对应多个主题
class ConcreteObserver(ObserverAPI):
# 给观察者绑定主题,同时把观察者添加到主题列表
def __init__(self, subject, name):
ObserverAPI.__init__(self, name)
# python3支持的父类调用
# super(ConcreteObserver, self).__init__(name)
# super().__init__(name)
self.subject = subject
subject.register(self)
# 观察者发出更新通知,不用单独告诉订阅者,由订阅者自行监听
def update(self, content):
print(self.__class__.__name__ + '::update() [subject.name = ' +
self.subject.name + ' content = ' + content + ']')

 
# ConcreteObserver2.py 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者可以对应不同的主题。
from src.ObserverAPI import ObserverAPI
# 具体的观察者实现类,也可以看成订阅者,关联对应的主题类。
# 不同的观察者可以对应不同的主题。
class ConcreteObserver2(ObserverAPI):
# 这里没有在构造器就绑定某个主题,而是从客户角度去注册观察者
# 观察者发出更新通知,观察者自行监听
# def update(self, content):
# print(self.__class__.__name__ + '::update() [content = ' + content +']')
pass

抽象主题类

 
# Subject.py 定义抽象主题类或者接口,供具体主题类继承
class Subject:
def __init__(self, name):
self.name = name
self.observers = []
def get_name(self):
return self.name
def set_name(self, name):
self.name = name
def register(self, observer):
print(self.__class__.__name__ + '::register() [observer = ' +
observer.__class__.__name__ + ']')
self.observers.append(observer)
def remove(self, observer):
self.observers.remove(observer)
# 通知由具体类来实现逻辑
def notify(self, name):
pass

具体主题类

 
// ConcreteSubject.py 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。
from src.Subject import Subject
# 观察者主题类,也是发布者,重写具体的通知方法。不同主题可以关联不同的观察者。
class ConcreteSubject(Subject):
# 不同的主题类有自己的通知方法,批量通知绑定的观察者
def notify(self, content):
print(self.__class__.__name__ + '::notify() [content = ' + content +
']')
for observer in self.observers:
observer.update(content)

测试调用

 
import sys
import os
os_path = os.getcwd()
sys.path.append(os_path)
from src.ConcreteSubject import ConcreteSubject
from src.ConcreteObserver import ConcreteObserver
from src.ConcreteObserver2 import ConcreteObserver2
def test():
'''
* 观察者模式应用非常广泛,主要是观察者提前绑定到发布者
* 当发布者发布消息时,批量广播通知,而无需逐一通知
* 观察者监听到消息后自己决定采取哪一种行为
'''
# 定义一个主题,也就是发布者
concrete_subject = ConcreteSubject('subject1')
# 再声明观察者,通过构造器注册到主题上
observer1 = ConcreteObserver(concrete_subject, 'observer1')
# 也可以单独给主题注册一个新的观察者
observer2 = ConcreteObserver2('observer2')
concrete_subject.register(observer2)
# 可以移除观察者对象
# concrete_subject.remove(observer1)
# 主题开始发布新通知,各观察者自动更新
concrete_subject.notify('hello, this is broadcast.')
if __name__ == '__main__':
print(__file__)
print("test start:")
test()

更多语言版本

不同语言实现设计模式:https://github.com/microwind/design-pattern

  

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

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

相关文章

PCI 总线树BUS 号

在一个处理器系统中&#xff0c;每一个host 主桥都推出一棵PCI 总线树。 一棵PCI 总线树中有多少个PCIB bridge , 就含有多少条PCI 总线。 系统软件在遍历当前PCI 总线树时&#xff0c;需要首先对这些PCI 总线进行编号&#xff0c;即初始化PCI 桥的primary, secondary 和Subord…

C++自学: virtual function

使用virtual关键字在base class中声明virtual function。你可以使用指针在derived class中调用和执行virtual function。 “virtual void MakeSound() 0;”这是一个pure virtual function。 有至少一个pure virtual function的class称为abstract class&#xff0c;所以Instr…

策略梯度方法

策略梯度方法 数学背景 给定一个标量函数 J ( θ ) J\left(\theta\right) J(θ)&#xff0c;利用梯度上升法&#xff0c;使其最大化&#xff0c;此时的 π θ \pi_\theta πθ​就是最优策略。 θ t 1 θ t α ∇ θ J ( θ t ) \theta_{t1}\theta_t\alpha \nabla_\theta…

Element Plus el-table 数据为空时自定义内容【默认为 No Data】

1. 通过 Table 属性设置 <div class"el-plus-table"><el-table empty-text"暂无数据" :data"tableData" style"width: 100%"><el-table-column prop"date" label"Date" width"180" /&g…

系统架构设计师---OSI七层协议

目录 OSI七层协议 各层主要功能和详细说明 Internet协议的主要协议及其层次关系

常用的请求 Content-Type 类型

application/json&#xff1a;用于发送 JSON 格式的数据&#xff0c;常用于 RESTful API 请求中。 application/x-www-form-urlencoded&#xff1a;在 POST 请求中以 URL 编码(key-value 对)的方式发送表单数据。 multipart/form-data&#xff1a;用于同时上传文件和其他表单…

vue + el-table 表格数据导出为excel表格

下载依赖 npm install --save xlsx file-saver引入插件 import * as XLSX from xlsx; import FileSaver from "file-saver";完整代码 <template><div class"administrativeCase-container"><div class"content-box"><di…

Java后端实现不用pagehelper。手写分页如何实现?

Java后端实现不用pagehelper。手写分页如何实现? 如果你不使用PageHelper这样的分页插件&#xff0c;你可以手动实现分页逻辑。下面是一个使用Java后端手写分页的示例&#xff1a; 首先&#xff0c;确定每页显示的数据量和当前页码。 int pageSize 10; // 每页显示的数据量…

移动通信系统的LMS自适应波束成形技术matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ..................................................................... idxx0; while idxx&…

Spring Bean的生命周期总结(包含面试题)

目录 一、Bean的初始化过程 1. 加载Spring Bean 2. 解析Bean的定义 3. Bean属性定义 4. BeanFactoryPostProcessor 扩展接口 5. 实例化Bean对象 6. Aware感知 7. 初始化方法 8. 后置处理 9. destroy 销毁 二、Bean的单例与多例模式 2.1 单例模式&#xff08;Sin…

游戏不再只是娱乐,更成为了一种学习和成长的途径

随着科技的飞速发展和游戏设计的创新&#xff0c;当下的游戏行业正经历着前所未有的繁荣时代。各种各样的游戏类型在不断涌现&#xff0c;为玩家们带来了丰富多彩的娱乐体验。这些火热的游戏类型不仅改变了我们的娱乐方式&#xff0c;还在无形中影响了我们的生活。 多元游戏类…

2023河南萌新联赛第(六)场:河南理工大学-F 爱睡大觉的小C

2023河南萌新联赛第&#xff08;六&#xff09;场&#xff1a;河南理工大学-F 爱睡大觉的小C https://ac.nowcoder.com/acm/contest/63602/F 文章目录 2023河南萌新联赛第&#xff08;六&#xff09;场&#xff1a;河南理工大学-F 爱睡大觉的小C题意解题思路 题意 新学期的概…

大数据平台中元数据库—MySQL的异常故障解决

本文的主要目标是解决大数据平台中元数据库MySQL的异常故障。通过分析应用响应缓慢的问题&#xff0c;找到了集群组件HIVE和元数据库MySQL的原因。通过日志分析、工具检测和专家指导等一系列方法&#xff0c; 最终确定问题的根源是大数据集群中租户的不规范使用所导致&#xff…

[Unity]Lua本地时间、倒计时和正计时。

惯例&#xff0c;直接上代码&#xff1a; --正计时开始时的时间戳 self.begin_time os.time() --倒计时时长&#xff0c;01:30:00 self.countdown_time 5400 --是否开始计时 self.is_update_local_time true--Unity Update function time_transition:update_local_timer()i…

Linux学习之iptables过滤规则的使用

cat /etc/redhat-release看到操作系统是CentOS Linux release 7.6.1810&#xff0c;uname -r看到内核版本是3.10.0-957.el7.x86_64&#xff0c;iptables --version可以看到iptables版本是v1.4.21。 iptables -t filter -A INPUT -s 10.0.0.8 -j ACCEPT会在最后一行插入。 10…

代码随想录day52

300最长递增子序列 class Solution { public:int lengthOfLIS(vector<int>& nums) {int piles 0; // 牌堆数初始化为 0vector<int> top(nums.size()); // 牌堆数组 topfor (int i 0; i < nums.size(); i) {int poker nums[i]; int left 0, right…

04 qt功能类、对话框类和文件操作

一 QT中时间和日期 时间 ---- QTime日期 ---- QDate对于Qt而言,在实际的开发过程中, 1)开发者可能知道所要使用的类 ---- >帮助手册 —>索引 -->直接输入类名进行查找 2)开发者可能不知道所要使用的类,只知道开发需求文档 ----> 帮助 手册,按下图操作: 1 …

Android 13像Settings一样获取SIM卡信息

一.背景 由于客户定制的Settings里面需要获取到SIM卡信息,所以需要实现此功能。 目录 一.背景 二.前提条件 三.调用api 二.前提条件 首先应用肯定要是系统应用,并且导入framework.jar包,具体可以参考: Android 应用自动开启辅助(无障碍)功能并使用辅助(无障碍)功能_…

python中的cnn:介绍和基本使用方法

python中的cnn&#xff1a;介绍和基本使用方法 卷积神经网络&#xff08;Convolutional Neural Networks&#xff0c;简称CNN&#xff09;是一种在图像识别、语音识别、自然语言处理等许多领域取得显著成功的深度学习模型。CNN的设计灵感来源于生物的视觉系统&#xff0c;由多…

WordPress更换域名后-后台无法进入,网站模版错乱,css失效,网页中图片不显示。完整解决方案(含宝塔设置)

我在实际解决问题时用到了 【简单暴力解决方案】的《方法一&#xff1a;修改wp-config.php》 和 【简单暴力-且特别粗暴-的解决方案】 更换域名时经常遇到的几个问题&#xff1a; 1、更换域名后&#xff0c;后台无法进入 2、更换域名后&#xff0c;网站模版错乱&#xff0c;c…