设计模式之【备忘录模式】,“后悔药”是可以有的
文章目录
一、什么是备忘录模式
备忘录模式(Memento Pattern)又称为快照模式(Snapshot Pattern)或令牌模式(Token Pattern),是指在不破坏封装的前提下,捕获一个对象的内部状态,并在对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态,属于行为型模式。
在软件系统中,备忘录模式可以为我们提供一种“后悔药”的机制,它通过存储系统各个历史状态的快照,使得我们可以在任一时刻将系统回滚到某一个历史状态。
备忘录模式本质是从发起人实体类(Originator)隔离存储功能,降低实体类的职责。同时由于存储信息(Memento)独立,且存储信息的实体交由管理类(Caretaker)管理,则可以通过为管理类扩展额外的功能对存储信息进行扩展操作(比如增加历史快照功能)。
1、备忘录模式使用场景
- 需要保存历史快照的场景。
- 希望在对象之外保存状态,且除了自己其他类对象无法访问状态保存具体内容。
比如,玩游戏时的中间结果的存档功能、如 Word、记事本、Photoshop,idea等软件在编辑时按Ctrl+Z 组合键,还有数据库中事务操作。
2、备忘录模式优缺点
优点:
- 提供了一种可以恢复状态的机制。当用户需要时能够比较方便地将数据恢复到某个历史的状态。
- 实现了内部状态的封装。除了创建它的发起人之外,其他对象都不能够访问这些状态信息。
- 简化了发起人类。发起人不需要管理和保存其内部状态的各个备份,所有状态信息都保存在备忘录中,并由管理者进行管理,这符合单一职责原则。
缺点:
- 资源消耗大。如果要保存的内部状态信息过多或者特别频繁,将会占用比较大的内存资源。
- 如果需要保存的状态过多时,每一次保存都会消耗很多内存。
3、备忘录模式的三大角色

备忘录模式的主要角色如下:
- 发起人(Originator)角色:记录当前时刻的内部状态信息,提供创建备忘录和恢复备忘录数据的功能,实现其他业务功能,它可以访问备忘录里的所有信息。
- 备忘录(Memento)角色:负责存储发起人的内部状态,在需要的时候提供这些内部状态给发起人,且防止发起人以外的对象访问。
- 管理者(Caretaker)角色:对备忘录进行管理,提供保存与获取备忘录的功能,但其不能对备忘录的内容进行访问与修改。
4、白箱备忘录和黑箱备忘录
备忘录有两个等效的接口:
- 窄接口:管理者(Caretaker)对象(和其他发起人对象之外的任何对象)看到的是备忘录的窄接口(narror Interface),这个窄接口只允许他把备忘录对象传给其他的对象。
- 宽接口:与管理者看到的窄接口相反,发起人对象可以看到一个宽接口(wide Interface),这个宽接口允许它读取所有的数据,以便根据这些数据恢复这个发起人对象的内部状态。
白箱备忘录使用的就是宽接口,白箱备忘录模式是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。
黑箱备忘录使用的是窄接口,将备忘录角色封装在发起人角色的内部形成一个私有的内部类,并实现窄接口。管理者只管理窄接口,这样可以屏蔽备忘录角色的细节。
5、思考:备份频率快,备份对象大的备忘录应该如何设计
假设每当有数据改动,我们都需要生成一个备份,以备之后恢复。如果需要备份的数据很大,这样高频率的备份,不管是对存储(内存或者硬盘)的消耗,还是对时间的消耗,都可能是无法接受的。想要解决这个问题,我们一般会采用“低频率全量备份”和“高频率增量备份”相结合的方法。
当我们需要恢复到某一时间点的备份的时候,如果这一时间点有做全量备份,我们直接拿来恢复就可以了。如果这一时间点没有对应的全量备份,我们就先找到最近的一次全量备份,然后用它来恢复,之后执行此次全量备份跟这一时间点之间的所有增量备份,也就是对应的操作或者数据变动。这样就能减少全量备份的数量和频率,减少对时间、内存的消耗。
其实很多设计原则和设计思想都是互通的,mysql的备份与恢复、redis的备份与恢复都是参考了这种实现原理。
二、实例
1、备忘录模式的一般写法
// 发起人角色
public class Originator {// 内部状态private String state;public String getState() {return this.state;}public void setState(String state) {this.state = state;}// 创建一个备忘录public Memento createMemento() {return new Memento(this.state);}// 从备忘录恢复public void restoreMemento(Memento memento) {this.setState(memento.getState());}
}
// 备忘录角色
public class Memento {private String state;public Memento(String state){this.state = state;}public String getState() {return this.state;}public void setState(String state) {this.state = state;}
}
// 管理者角色
public class Caretaker {// 备忘录对象private Memento memento;public Memento getMemento() {return this.memento;}public void storeMemento(Memento memento) {this.memento = memento;}}
// 测试类
public class Test {public static void main(String[] args) {//来一个发起人Originator originator = new Originator();//来一个备忘录管理员Caretaker caretaker = new Caretaker();//管理员存储发起人的备忘录caretaker.storeMemento(originator.createMemento());//发起人从管理员获取备忘录进行回滚originator.restoreMemento(caretaker.getMemento());}
}
2、使用栈管理富文本编辑器
我们使用富文本编辑器时,会经常写入、撤销、修改。因此我们需要将每一时刻的修改记录都要保存在草稿箱中。
// 发起人角色编辑器
public class Editor {private String title;private String content;private String imgs;public Editor(String title, String content, String imgs) {this.title = title;this.content = content;this.imgs = imgs;}public String getTitle() {return title;}public String getContent() {return content;}public String getImgs() {return imgs;}public void setTitle(String title) {this.title = title;}public void setContent(String content) {this.content = content;}public void setImgs(String imgs) {this.imgs = imgs;}public ArticleMemento saveToMemento(){ArticleMemento articleMemento = new ArticleMemento(this.title,this.content,this.imgs);return articleMemento;}public void undoFromMemento(ArticleMemento articleMemento){this.title = articleMemento.getTitle();this.content = articleMemento.getContent();this.imgs = articleMemento.getImgs();}@Overridepublic String toString() {return "Editor{" +"title='" + title + '\'' +", content='" + content + '\'' +", imgs='" + imgs + '\'' +'}';}
}
// 备忘录角色
public class ArticleMemento {private String title;private String content;private String imgs;public ArticleMemento(String title, String content, String imgs) {this.title = title;this.content = content;this.imgs = imgs;}public String getTitle() {return title;}public String getContent() {return content;}public String getImgs() {return imgs;}@Overridepublic String toString() {return "ArticleMemento{" +"title='" + title + '\'' +", content='" + content + '\'' +", imgs='" + imgs + '\'' +'}';}
}
// 管理角色 草稿箱
public class DraftsBox {private final Stack<ArticleMemento> STACK = new Stack<ArticleMemento>();public ArticleMemento getMemento(){ArticleMemento articleMemento = STACK.pop();return articleMemento;}public void addMemento(ArticleMemento articleMemento){STACK.push(articleMemento);}}
草稿箱中定义的Stack类是Vector的一个子类,它实现了一个标准的后进先出的栈。主要定义了以下方法:
| 方法定义 | 方法描述 |
|---|---|
| boolean empty() | 测试栈是否为空 |
| Object peek() | 查看栈顶对象,但不从栈中移除它 |
| Object pop() | 移除栈顶对象,并作为此函数的返回值 |
| Object push(Object element) | 把对象压入栈顶 |
| int search(Object element) | 返回对象在栈中的位置,以1为基数 |
// 测试类
public class Test {public static void main(String[] args) {DraftsBox draftsBox = new DraftsBox();Editor editor = new Editor("标题1","内容1","图片1");ArticleMemento articleMemento = editor.saveToMemento();draftsBox.addMemento(articleMemento);System.out.println("标题:" + editor.getTitle() + "\n" +"内容:" + editor.getContent() + "\n" +"插图:" + editor.getImgs() + "\n暂存成功");System.out.println("完整的信息" + editor);System.out.println("==========首次修改文章===========");editor.setTitle("标题2");editor.setContent("内容2");editor.setImgs("图片2");System.out.println("==========首次修改文章完成===========");System.out.println("完整的信息" + editor);articleMemento = editor.saveToMemento();draftsBox.addMemento(articleMemento);System.out.println("==========保存到草稿箱===========");System.out.println("==========第2次修改文章===========");editor.setTitle("标题3");editor.setContent("内容3");editor.setImgs("图片3");System.out.println("完整的信息" + editor);System.out.println("==========第2次修改文章完成===========");System.out.println("==========第1次撤销===========");articleMemento = draftsBox.getMemento();editor.undoFromMemento(articleMemento);System.out.println("完整的信息" + editor);System.out.println("==========第1次撤销完成===========");System.out.println("==========第2次撤销===========");articleMemento = draftsBox.getMemento();editor.undoFromMemento(articleMemento);System.out.println("完整的信息" + editor);System.out.println("==========第2次撤销完成===========");}
}
执行结果:

3、游戏状态恢复案例
游戏中的某个场景,一游戏角色有生命力、攻击力、防御力等数据,在打Boss前和后一定会不一样的,我们允许玩家如果感觉与Boss决斗的效果不理想可以让游戏恢复到决斗之前的状态。
(1)“白箱”备忘录模式
备忘录角色对任何对象都提供一个接口,即宽接口,备忘录角色的内部所存储的状态就对所有对象公开。类图如下:

//游戏角色类
public class GameRole {private int vit; //生命力private int atk; //攻击力private int def; //防御力//初始化状态public void initState() {this.vit = 100;this.atk = 100;this.def = 100;}//战斗public void fight() {this.vit = 0;this.atk = 0;this.def = 0;}//保存角色状态public RoleStateMemento saveState() {return new RoleStateMemento(vit, atk, def);}//回复角色状态public void recoverState(RoleStateMemento roleStateMemento) {this.vit = roleStateMemento.getVit();this.atk = roleStateMemento.getAtk();this.def = roleStateMemento.getDef();}public void stateDisplay() {System.out.println("角色生命力:" + vit);System.out.println("角色攻击力:" + atk);System.out.println("角色防御力:" + def);}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}
}
//游戏状态存储类(备忘录类)
public class RoleStateMemento {private int vit;private int atk;private int def;public RoleStateMemento(int vit, int atk, int def) {this.vit = vit;this.atk = atk;this.def = def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}
}
//角色状态管理者类
public class RoleStateCaretaker {private RoleStateMemento roleStateMemento;public RoleStateMemento getRoleStateMemento() {return roleStateMemento;}public void setRoleStateMemento(RoleStateMemento roleStateMemento) {this.roleStateMemento = roleStateMemento;}
}
//测试类
public class Client {public static void main(String[] args) {System.out.println("------------大战Boss前------------");//大战Boss前GameRole gameRole = new GameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();roleStateCaretaker.setRoleStateMemento(gameRole.saveState());System.out.println("------------大战Boss后------------");//大战Boss时,损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println("------------恢复之前状态------------");//恢复之前状态gameRole.recoverState(roleStateCaretaker.getRoleStateMemento());gameRole.stateDisplay();}
}
分析:白箱备忘录模式是破坏封装性的。但是通过程序员自律,同样可以在一定程度上实现模式的大部分用意。
(2)“黑箱”备忘录模式
备忘录角色对发起人对象提供一个宽接口,而为其他对象提供一个窄接口。在Java语言中,实现双重接口的办法就是将备忘录类设计成发起人类的内部成员类。
将 RoleStateMemento 设为 GameRole 的内部类,从而将 RoleStateMemento 对象封装在GameRole 里面;在外面提供一个标识接口 Memento 给 RoleStateCaretaker 及其他对象使用。
这样 GameRole 类看到的是 RoleStateMemento 所有的接口,而 RoleStateCaretaker 及其他对象看到的仅仅是标识接口 Memento 所暴露出来的接口,从而维护了封装型。类图如下:

// 窄接口 Memento ,这是一个标识接口,因此没有定义出任何的方法
public interface Memento {
}
// 定义发起人类 GameRole ,并在内部定义备忘录内部类 RoleStateMemento (该内部类设置为私有的)
//游戏角色类
public class GameRole {private int vit; //生命力private int atk; //攻击力private int def; //防御力//初始化状态public void initState() {this.vit = 100;this.atk = 100;this.def = 100;}//战斗public void fight() {this.vit = 0;this.atk = 0;this.def = 0;}//保存角色状态public Memento saveState() {return new RoleStateMemento(vit, atk, def);}//回复角色状态public void recoverState(Memento memento) {RoleStateMemento roleStateMemento = (RoleStateMemento) memento;this.vit = roleStateMemento.getVit();this.atk = roleStateMemento.getAtk();this.def = roleStateMemento.getDef();}public void stateDisplay() {System.out.println("角色生命力:" + vit);System.out.println("角色攻击力:" + atk);System.out.println("角色防御力:" + def);}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}// 备忘录角色内部类private class RoleStateMemento implements Memento {private int vit;private int atk;private int def;public RoleStateMemento(int vit, int atk, int def) {this.vit = vit;this.atk = atk;this.def = def;}public int getVit() {return vit;}public void setVit(int vit) {this.vit = vit;}public int getAtk() {return atk;}public void setAtk(int atk) {this.atk = atk;}public int getDef() {return def;}public void setDef(int def) {this.def = def;}}
}
负责人角色类 RoleStateCaretaker 能够得到的备忘录对象是以 Memento 为接口的,由于这个接口仅仅是一个标识接口,因此负责人角色不可能改变这个备忘录对象的内容
//角色状态管理者类
public class RoleStateCaretaker {private Memento memento;public Memento getMemento() {return memento;}public void setMemento(Memento memento) {this.memento = memento;}
}
// 测试类
public class Client {public static void main(String[] args) {System.out.println("------------大战Boss前------------");//大战Boss前GameRole gameRole = new GameRole();gameRole.initState();gameRole.stateDisplay();//保存进度RoleStateCaretaker roleStateCaretaker = new RoleStateCaretaker();roleStateCaretaker.setMemento(gameRole.saveState());System.out.println("------------大战Boss后------------");//大战Boss时,损耗严重gameRole.fight();gameRole.stateDisplay();System.out.println("------------恢复之前状态------------");//恢复之前状态gameRole.recoverState(roleStateCaretaker.getMemento());gameRole.stateDisplay();}
}
相关文章:
设计模式之【备忘录模式】,“后悔药”是可以有的
文章目录 一、什么是备忘录模式1、备忘录模式使用场景2、备忘录模式优缺点3、备忘录模式的三大角色4、白箱备忘录和黑箱备忘录5、思考:备份频率快,备份对象大的备忘录应该如何设计 二、实例1、备忘录模式的一般写法2、使用栈管理富文本编辑器3、游戏状态…...
ATECLOUD云测试平台新能源电机测试系统:高效、可扩展的测试利器
随着全球对环境保护的日益重视,新能源的发展越来越受到关注。电动汽车作为新能源领域的重要组成部分,其性能和质量对于消费者来说至关重要。为了确保电动汽车的性能和质量,测试系统平台解决方案变得越来越重要。本文将介绍一种基于ATECLOUD智…...
项目随机问题笔记
一、前端项目启动的命令 启动项目依赖:npm install 安装cross-env模块:npm i cross-env --save-dev 启动报错时试试这个 npm install node-sass (安装sass) 启动项目命令1 npm run dev 启动项目命令2 npm run start 启动项目命令3 npm start 二、前…...
Linux网络编程之recv函数
功能 recv 函数的功能就是从套接字中接收数据。 头文件 #include <sys/types.h> #include <sys/socket.h>原型 ssize_t recv(int sockfd, void *buf, size_t len, int flags);参数 参数描述sockfdsocket 文件描述符buf接收数据缓冲区len接收数据缓冲区的大小f…...
ChatGPT免费使用的方法有哪些?
目录 一、ChatGpt是什么? 二、ChatGPT国内免费使用的方法: 第一点:电脑端 第二点:手机端 三、结语: 一、ChatGpt是什么? ChatGPt是美国OpenAI [1] 研发的聊天机器人程序 。更是人工智能技术驱动的自然语…...
【华为OD机试】找朋友【2023 B卷|100分】
华为OD机试- 题目列表 2023Q1 点这里!! 2023华为OD机试-刷题指南 点这里!! 题目描述 在学校中,N个小朋友站成一队, 第i个小朋友的身高为height[i], 第i个小朋友可以看到的第一个比自己身高更高的小朋友j,那么j是i的好朋友(要求j > i)。 请重新生成一个列表,对应…...
【教学类-35-01】带笔画步骤图的描字(姓氏)(A4整张)
作品展示: 1、图片一行(0-6):文字简单,写3*412个字 2、图片2行(6-12):笔画适中,写3*39个字 3、图片3行(12-18):笔画适中,…...
关于PyQt5的环境搭建
目录 一、需要的环境 二、安装python 1、python安装链接 三、安装PyQt5 1、使用豆瓣的镜像 2、配置环境变量 四、安装pycharm 1、pycharm官网链接 五、配置环境 1、找到设置 2、添加designer 3、配置ui 4、配置rc 六、注意问题 一、需要的环境 1、安装好python安装…...
rsync+inotfy实时同步
rsyncinotfy实时同步 目录 一、服务器端 二、客户端 一、服务器端 1、安装网站服务,启动,但是不写首页文件 yum -y install httpd 2、安装raync服务 yum -y install rsync 3、修改主配置文件 (/etc/rsyncd.conf) uid root gi…...
Python代码写好了怎么运行
Python代码写好了怎么运行?相信问这样问题的朋友一定是刚刚入门Python的初学者。本文就来为大家详细讲讲如何运行Python代码。 一般来讲,运行Python代码的方式有两种,一是在Python交互式命令行下运行;另一种是使用文本编辑器&…...
2023 年的 Web Worker 项目实践
目录 前言 引入 Web Worker Worker 实践 Worker 到底有多难用 类库调研 有类库加持的 worker 现状 向着舒适无感的 worker 编写前进 1. 抽取依赖,管理编译和更新: 2. 定义公共调用函数,引入所打包的依赖并串联流程: 3. …...
C++的最后一道坎 | 百万年薪的程序员
| 导语 C 的起源可以追溯到 40 年前,但它仍然是当今使用最广泛的编程语言之一,C发明人Bjarne Stroustrup 一开始没想到 C 会获得如此大的成功,他说:“C 的成功显然令人惊讶。我认为它的成功取决于其最初的设计目标,就是…...
Unity的OnOpenAsset:深入解析与实用案例
Unity OnOpenAsset 在Unity中,OnOpenAsset是一个非常有用的回调函数,它可以在用户双击资源文件时自动打开一个编辑器窗口。这个回调函数可以用于自定义资源编辑,提高工作效率。本文将介绍OnOpenAsset的使用方法,并提供三个使用例…...
【Netty】Netty 程序引导类(九)
文章目录 前言一、引导程序类二、AbstractBootStrap 抽象类三、Bootstrap 类四、ServerBootstrap 类五、引导服务器5.1、 实例化引导程序类5.2、设置 EventLoopGroup5.3、指定 Channel 类型5.4、指定 ChannelHandler5.5、设置 Channel 选项5.6、绑定端口启动服务 六、引导客户端…...
如何使用进行MQ中间件接口测试
进行MQ中间件接口测试时,需要按以下步骤进行: 1.配置测试环境 搭建MQ服务器环境,并确保连接配置正确,以及客户端SDK等相关依赖库和组件已安装并配置正确。 2.制定测试计划 测试人员需要根据具体业务场景和测试目的,制…...
Zebec生态进展迅速,频被BitFlow、Matryx DAO等蹭热度碰瓷
进入到 2023 年以来, Zebec 生态的整体发展突飞猛进,除了流支付协议 Zebec Protocol 不断通过收购来扩大自身流支付业务、与万事达等合作推出 Zebec Card 等在支付业务上,实现进展外,其社区驱动的Layer3 模块化链 Nautilus Chain …...
7种PCB走线方式
01电源布局布线相关 数字电路很多时候需要的电流是不连续的,所以对一些高速器件就会产生浪涌电流。 如果电源走线很长,则由于浪涌电流的存在进而会导致高频噪声,而此高频噪声会引入到其他信号中去。 而在高速电路中必然会存在寄生电感和寄…...
Rabbit SpringBoot高级用法
Rabbit高级用法 一、Rabbit Springboot集成1.1 引入依赖1.2 添加配置1.3 添加Config1.4 编写Consumer1.5 发送消息 二、Rabbit 高级用法2.1 消息发送前置处理器2.2 消息发送确认机制2.3 消息接收后处理器2.4 事务消息 一、Rabbit Springboot集成 1.1 引入依赖 <dependency…...
找不到vcruntime140.dll,无法继续执行代码?多种解决方法解析
找不到vcruntime140.dll,无法继续执行代码?当你在尝试运行某个程序时,突然弹出一条错误提示框,告诉你无法继续执行代码,因为找不到vcruntime140.dll。这个问题很常见,但是它可能会让你感到困惑和疑惑。这篇文章将详细介…...
自然语言处理实战项目8- BERT模型的搭建,训练BERT实现实体抽取识别的任务
大家好,我是微学AI,今天给大家介绍一下自然语言处理实战项目8- BERT模型的搭建,训练BERT实现实体抽取识别的任务。BERT模型是一种用于自然语言处理的深度学习模型,它可以通过训练来理解单词之间的上下文关系,从而为下游…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频
使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
【Linux】Linux 系统默认的目录及作用说明
博主介绍:✌全网粉丝23W,CSDN博客专家、Java领域优质创作者,掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围:SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...
苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会
在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...
嵌入式常见 CPU 架构
架构类型架构厂商芯片厂商典型芯片特点与应用场景PICRISC (8/16 位)MicrochipMicrochipPIC16F877A、PIC18F4550简化指令集,单周期执行;低功耗、CIP 独立外设;用于家电、小电机控制、安防面板等嵌入式场景8051CISC (8 位)Intel(原始…...
