【Rust】——面向对象设计模式的实现

 🎼个人主页:【Y小夜】

😎作者简介:一位双非学校的大二学生,编程爱好者,

专注于基础和实战分享,欢迎私信咨询!

🎆入门专栏:🎇【MySQL,Java基础,Rust】

🎈热门专栏:🎊【Python,Javaweb,Vue框架】

感谢您的点赞、关注、评论、收藏、是对我最大的认可和支持!❤️

学习推荐:

    人工智能是一个涉及数学、计算机科学、数据科学、机器学习、神经网络等多个领域的交叉学科,其学习曲线相对陡峭,对初学者来说可能会有一定的挑战性。幸运的是,随着互联网教育资源的丰富,现在有大量优秀的在线平台和网站提供了丰富的人工智能学习材料,包括视频教程、互动课程、实战项目等,这些资源无疑为学习者打开了一扇通往人工智能世界的大门。

        前些天发现了一个巨牛的人工智能学习网站:前言 – 人工智能教程通俗易懂,风趣幽默,忍不住分享一下给大家。

目录

🎯定义Post并新建一个草案状态的实例

🎯存放博文内容的文本

🎯确保博文草案的内容是空的

🎯请求审核博文来改变其状态

🎯增加改变content行为的approve方法

🎯状态模式的权衡取舍

🎯将状态和行为编码为类型

🎯实现状态转移为不同类型的转换


        状态模式state pattern)是一个面向对象设计模式。该模式的关键在于定义一系列值的内含状态。这些状态体现为一系列的 状态对象,同时值的行为随着其内部状态而改变。我们将编写一个博客发布结构体的例子,它拥有一个包含其状态的字段,这是一个有着 "draft"、"review" 或 "published" 的状态对象

        状态对象共享功能:当然,在 Rust 中使用结构体和 trait 而不是对象和继承。每一个状态对象负责其自身的行为,以及该状态何时应当转移至另一个状态。持有一个状态对象的值对于不同状态的行为以及何时状态转移毫不知情。

        使用状态模式的优点在于,程序的业务需求改变时,无需改变值持有状态或者使用值的代码。我们只需更新某个状态对象中的代码来改变其规则,或者是增加更多的状态对象。

        首先我们将以一种更加传统的面向对象的方式实现状态模式,接着使用一种更 Rust 一点的方式。让我们使用状态模式增量式地实现一个发布博文的工作流以探索这个概念。

这个博客的最终功能看起来像这样:

  1. 博文从空白的草案开始。
  2. 一旦草案完成,请求审核博文。
  3. 一旦博文过审,它将被发表。
  4. 只有被发表的博文的内容会被打印,这样就不会意外打印出没有被审核的博文的文本。

        任何其他对博文的修改尝试都是没有作用的。例如,如果尝试在请求审核之前通过一个草案博文,博文应该保持未发布的状态。

        这是一个我们将要在一个叫做 blog 的库 crate 中实现的 API 的示例。这段代码还不能编译,因为还未实现 blog

use blog::Post;fn main() {let mut post = Post::new();post.add_text("I ate a salad for lunch today");assert_eq!("", post.content());post.request_review();assert_eq!("", post.content());post.approve();assert_eq!("I ate a salad for lunch today", post.content());
}

        我们希望允许用户使用 Post::new 创建一个新的博文草案。也希望能在草案阶段为博文编写一些文本。如果在审批之前尝试立刻获取博文的内容,不应该获取到任何文本因为博文仍然是草案。一个好的单元测试将是断言草案博文的 content 方法返回空字符串,不过我们并不准备为这个例子编写单元测试。

        接下来,我们希望能够请求审核博文,而在等待审核的阶段 content 应该仍然返回空字符串。最后当博文审核通过,它应该被发表,这意味着当调用 content 时博文的文本将被返回。

        注意我们与 crate 交互的唯一的类型是 Post。这个类型会使用状态模式并会存放处于三种博文所可能的状态之一的值 —— 草案,等待审核和发布。状态上的改变由 Post 类型内部进行管理。状态依库用户对 Post 实例调用的方法而改变,但是不能直接管理状态变化。这也意味着用户不会在状态上犯错,比如在过审前发布博文。

🎯定义Post并新建一个草案状态的实例

        让我们开始实现这个库吧!我们知道需要一个公有 Post 结构体来存放一些文本,所以让我们从结构体的定义和一个创建 Post 实例的公有关联函数 new 开始,

   Post 将在私有字段 state 中存放一个 Option<T> 类型的 trait 对象 Box<dyn State>。稍后将会看到为何 Option<T> 是必须的。

pub struct Post {state: Option<Box<dyn State>>,content: String,
}impl Post {pub fn new() -> Post {Post {state: Some(Box::new(Draft {})),content: String::new(),}}
}trait State {}struct Draft {}impl State for Draft {}

   State trait 定义了所有不同状态的博文所共享的行为,这个状态对象是 DraftPendingReview 和 Published,它们都会实现 State 状态。现在这个 trait 并没有任何方法,同时开始将只定义 Draft 状态因为这是我们希望博文的初始状态。

        当创建新的 Post 时,我们将其 state 字段设置为一个存放了 Box 的 Some 值。这个 Box 指向一个 Draft 结构体新实例。这确保了无论何时新建一个 Post 实例,它都会从草案开始。因为 Post 的 state 字段是私有的,也就无法创建任何其他状态的 Post 了!。Post::new 函数中将 content 设置为新建的空 String

🎯存放博文内容的文本

展示了我们希望能够调用一个叫做 add_text 的方法并向其传递一个 &str 来将文本增加到博文的内容中。选择实现为一个方法而不是将 content 字段暴露为 pub 。这意味着之后可以实现一个方法来控制 content 字段如何被读取。

impl Post {// --snip--pub fn add_text(&mut self, text: &str) {self.content.push_str(text);}
}

  add_text 获取一个 self 的可变引用,因为需要改变调用 add_text 的 Post 实例。接着调用 content 中的 String 的 push_str 并传递 text 参数来保存到 content 中。这不是状态模式的一部分,因为它的行为并不依赖博文所处的状态。add_text 方法完全不与 state 状态交互,不过这是我们希望支持的行为的一部分。

🎯确保博文草案的内容是空的

        即使调用 add_text 并向博文增加一些内容之后,我们仍然希望 content 方法返回一个空字符串 slice,因为博文仍然处于草案状态,如示例 17-11 的第 8 行所示。现在让我们使用能满足要求的最简单的方式来实现 content 方法:总是返回一个空字符串 slice。当实现了将博文状态改为发布的能力之后将改变这一做法。但是目前博文只能是草案状态,这意味着其内容应该总是空的。

impl Post {// --snip--pub fn content(&self) -> &str {""}
}

🎯请求审核博文来改变其状态

        接下来需要增加请求审核博文的功能,这应当将其状态由 Draft 改为 PendingReview

impl Post {// --snip--pub fn request_review(&mut self) {if let Some(s) = self.state.take() {self.state = Some(s.request_review())}}
}trait State {fn request_review(self: Box<Self>) -> Box<dyn State>;
}struct Draft {}impl State for Draft {fn request_review(self: Box<Self>) -> Box<dyn State> {Box::new(PendingReview {})}
}struct PendingReview {}impl State for PendingReview {fn request_review(self: Box<Self>) -> Box<dyn State> {self}
}

        这里为 Post 增加一个获取 self 可变引用的公有方法 request_review。接着在 Post 的当前状态下调用内部的 request_review 方法,并且第二个 request_review 方法会消费当前的状态并返回一个新状态。

        这里给 State trait 增加了 request_review 方法;所有实现了这个 trait 的类型现在都需要实现 request_review 方法。注意不同于使用 self、 &self 或者 &mut self 作为方法的第一个参数,这里使用了 self: Box<Self>。这个语法意味着该方法只可在持有这个类型的 Box 上被调用。这个语法获取了 Box<Self> 的所有权使老状态无效化,以便 Post 的状态值可转换为一个新状态。

        为了消费老状态,request_review 方法需要获取状态值的所有权。这就是 Post 的 state 字段中 Option 的来历:调用 take 方法将 state 字段中的 Some 值取出并留下一个 None,因为 Rust 不允许结构体实例中存在值为空的字段。这使得我们将 state 的值移出 Post 而不是借用它。接着我们将博文的 state 值设置为这个操作的结果。

        我们需要将 state 临时设置为 None 来获取 state 值,即老状态的所有权,而不是使用 self.state = self.state.request_review(); 这样的代码直接更新状态值。这确保了当 Post 被转换为新状态后不能再使用老 state 值。

   Draft 的 request_review 方法需要返回一个新的,装箱的 PendingReview 结构体的实例,其用来代表博文处于等待审核状态。结构体 PendingReview 同样也实现了 request_review 方法,不过它不进行任何状态转换。相反它返回自身,因为当我们请求审核一个已经处于 PendingReview 状态的博文,它应该继续保持 PendingReview 状态。

🎯增加改变content行为的approve方法

approve 方法将与 request_review 方法类似:它会将 state 设置为审核通过时应处于的状态

impl Post {// --snip--pub fn approve(&mut self) {if let Some(s) = self.state.take() {self.state = Some(s.approve())}}
}trait State {fn request_review(self: Box<Self>) -> Box<dyn State>;fn approve(self: Box<Self>) -> Box<dyn State>;
}struct Draft {}impl State for Draft {// --snip--fn approve(self: Box<Self>) -> Box<dyn State> {self}
}struct PendingReview {}impl State for PendingReview {// --snip--fn approve(self: Box<Self>) -> Box<dyn State> {Box::new(Published {})}
}struct Published {}impl State for Published {fn request_review(self: Box<Self>) -> Box<dyn State> {self}fn approve(self: Box<Self>) -> Box<dyn State> {self}
}

        这里为 State trait 增加了 approve 方法,并新增了一个实现了 State 的结构体,Published 状态。

        类似于 PendingReview 中 request_review 的工作方式,如果对 Draft 调用 approve 方法,并没有任何效果,因为它会返回 self。当对 PendingReview 调用 approve 时,它返回一个新的、装箱的 Published 结构体的实例。Published 结构体实现了 State trait,同时对于 request_review 和 approve 两方法来说,它返回自身,因为在这两种情况博文应该保持 Published 状态。

impl Post {// --snip--pub fn content(&self) -> &str {self.state.as_ref().unwrap().content(self)}// --snip--
}

        因为目标是将所有像这样的规则保持在实现了 State 的结构体中,我们将调用 state 中的值的 content 方法并传递博文实例(也就是 self)作为参数。接着返回 state 值的 content 方法的返回值。

        这里调用 Option 的 as_ref 方法是因为需要 Option 中值的引用而不是获取其所有权。因为 state 是一个 Option<Box<dyn State>>,调用 as_ref 会返回一个 Option<&Box<dyn State>>。如果不调用 as_ref,将会得到一个错误,因为不能将 state 移动出借用的 &self 函数参数。

        接着我们就有了一个 &Box<dyn State>,当调用其 content 时,Deref 强制转换会作用于 & 和 Box ,这样最终会调用实现了 State trait 的类型的 content 方法。

trait State {// --snip--fn content<'a>(&self, post: &'a Post) -> &'a str {""}
}// --snip--
struct Published {}impl State for Published {// --snip--fn content<'a>(&self, post: &'a Post) -> &'a str {&post.content}
}

        这里增加了一个 content 方法的默认实现来返回一个空字符串 slice。这意味着无需为 Draft 和 PendingReview 结构体实现 content 了。Published 结构体会覆盖 content 方法并会返回 post.content 的值。

🎯状态模式的权衡取舍

        我们展示了 Rust 是能够实现面向对象的状态模式的,以便能根据博文所处的状态来封装不同类型的行为。Post 的方法并不知道这些不同类型的行为。通过这种组织代码的方式,要找到所有已发布博文的不同行为只需查看一处代码:Published 的 State trait 的实现。

        如果要创建一个不使用状态模式的替代实现,则可能会在 Post 的方法中,或者甚至于在 main 代码中用到 match 语句,来检查博文状态并在这里改变其行为。这意味着需要查看很多位置来理解处于发布状态的博文的所有逻辑!这在增加更多状态时会变得更糟:每一个 match 语句都会需要另一个分支。

        对于状态模式来说,Post 的方法和使用 Post 的位置无需 match 语句,同时增加新状态只涉及到增加一个新 struct 和为其实现 trait 的方法。

这个实现易于扩展增加更多功能。为了体会使用此模式维护代码的简洁性,请尝试如下一些建议:

  • 增加 reject 方法将博文的状态从 PendingReview 变回 Draft
  • 在将状态变为 Published 之前需要两次 approve 调用
  • 只允许博文处于 Draft 状态时增加文本内容。提示:让状态对象负责内容可能发生什么改变,但不负责修改 Post

        状态模式的一个缺点是因为状态实现了状态之间的转换,一些状态会相互联系。如果在 PendingReview 和 Published 之间增加另一个状态,比如 Scheduled,则不得不修改 PendingReview 中的代码来转移到 Scheduled。如果 PendingReview 无需因为新增的状态而改变就更好了,不过这意味着切换到另一种设计模式。

        另一个缺点是我们会发现一些重复的逻辑。为了消除它们,可以尝试为 State trait 中返回 self 的 request_review 和 approve 方法增加默认实现,不过这会违反对象安全性,因为 trait 不知道 self 具体是什么。我们希望能够将 State 作为一个 trait 对象,所以需要其方法是对象安全的。

        另一个重复是 Post 中 request_review 和 approve 这两个类似的实现。它们都委托调用了 state 字段中 Option 值的同一方法,并在结果中为 state 字段设置了新值。如果 Post 中的很多方法都遵循这个模式,我们可能会考虑定义一个宏来消除重复(查看第十九章的 “宏” 部分)。

        完全按照面向对象语言的定义实现这个模式并没有尽可能地利用 Rust 的优势。让我们看看一些代码中可以做出的修改,来将无效的状态和状态转移变为编译时错误。

🎯将状态和行为编码为类型

        我们将展示如何稍微反思状态模式来进行一系列不同的权衡取舍。不同于完全封装状态和状态转移使得外部代码对其毫不知情,我们将状态编码进不同的类型。如此,Rust 的类型检查就会将任何在只能使用发布博文的地方使用草案博文的尝试变为编译时错误。

fn main() {let mut post = Post::new();post.add_text("I ate a salad for lunch today");assert_eq!("", post.content());
}

        我们仍然希望能够使用 Post::new 创建一个新的草案博文,并能够增加博文的内容。不过不同于存在一个草案博文时返回空字符串的 content 方法,我们将使草案博文完全没有 content 方法。这样如果尝试获取草案博文的内容,将会得到一个方法不存在的编译错误。这使得我们不可能在生产环境意外显示出草案博文的内容,因为这样的代码甚至就不能编译。

pub struct Post {content: String,
}pub struct DraftPost {content: String,
}impl Post {pub fn new() -> DraftPost {DraftPost {content: String::new(),}}pub fn content(&self) -> &str {&self.content}
}impl DraftPost {pub fn add_text(&mut self, text: &str) {self.content.push_str(text);}
}

  Post 和 DraftPost 结构体都有一个私有的 content 字段来储存博文的文本。这些结构体不再有 state 字段因为我们将状态编码改为结构体类型。Post 将代表发布的博文,它有一个返回 content 的 content 方法。

        仍然有一个 Post::new 函数,不过不同于返回 Post 实例,它返回 DraftPost 的实例。现在不可能创建一个 Post 实例,因为 content 是私有的同时没有任何函数返回 Post

🎯实现状态转移为不同类型的转换

        那么如何得到发布的博文呢?我们希望强制执行的规则是草案博文在可以发布之前必须被审核通过。等待审核状态的博文应该仍然不会显示任何内容。让我们通过增加另一个结构体 PendingReviewPost 来实现这个限制,在 DraftPost 上定义 request_review 方法来返回 PendingReviewPost,并在 PendingReviewPost 上定义 approve 方法来返回 Post

impl DraftPost {// --snip--pub fn request_review(self) -> PendingReviewPost {PendingReviewPost {content: self.content,}}
}pub struct PendingReviewPost {content: String,
}impl PendingReviewPost {pub fn approve(self) -> Post {Post {content: self.content,}}
}

  request_review 和 approve 方法获取 self 的所有权,因此会消费 DraftPost 和 PendingReviewPost 实例,并分别转换为 PendingReviewPost 和发布的 Post。这样在调用 request_review 之后就不会遗留任何 DraftPost 实例,后者同理。PendingReviewPost 并没有定义 content 方法,所以尝试读取其内容会导致编译错误,DraftPost 同理。因为唯一得到定义了 content 方法的 Post 实例的途径是调用 PendingReviewPost 的 approve 方法,而得到 PendingReviewPost 的唯一办法是调用 DraftPost 的 request_review 方法,现在我们就将发博文的工作流编码进了类型系统。

        这也意味着不得不对 main 做出一些小的修改。因为 request_review 和 approve 返回新实例而不是修改被调用的结构体,所以我们需要增加更多的 let post = 覆盖赋值来保存返回的实例。也不再能断言草案和等待审核的博文的内容为空字符串了,我们也不再需要它们:不能编译尝试使用这些状态下博文内容的代码。

use blog::Post;fn main() {let mut post = Post::new();post.add_text("I ate a salad for lunch today");let post = post.request_review();let post = post.approve();assert_eq!("I ate a salad for lunch today", post.content());
}

        不得不修改 main 来重新赋值 post 使得这个实现不再完全遵守面向对象的状态模式:状态间的转换不再完全封装在 Post 实现中。然而,得益于类型系统和编译时类型检查,我们得到了的是无效状态是不可能的!这确保了某些特定的 bug,比如显示未发布博文的内容,将在部署到生产环境之前被发现。

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

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

相关文章

算法题--华为od机试考试(围棋的气、用连续自然数之和来表达整数、亲子游戏)

目录 围棋的气 题目描述 输入描述 示例1 输入 输出 解析 答案 用连续自然数之和来表达整数 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入 输出 解析 答案 亲子游戏 题目描述 输入描述 输出描述 示例1 输入 输出 说明 示例2 输入…

04 uboot 编译与调试

新手不需要详细掌握 uboot,只需要知道它是一个什么东西即可,工作中也只是改一些参数而已。 1、uboot 是什么 Linux 系统要启动就必须需要一个 bootloader 程序,也就说芯片上电以后先运行一段 bootloader 程序。这段 bootloader 程序会先初始化 DDR 等外设,然后将 Linux 内…

李飞飞解读创业方向:「空间智能」

在AI领域&#xff0c;李飞飞教授一直是一个举足轻重的存在。她的研究和见解不仅推动了计算机视觉的发展&#xff0c;更对人工智能的未来方向产生了深远的影响。在最近的一次演讲中&#xff0c;李飞飞详细解读了她对于「空间智能」的见解。本文将对她的演讲内容进行详细解读&…

正确挑选百兆超薄款工业级网络/脉冲变压器(网络隔离滤波器)

Hqst华强盛&#xff08;石门盈盛电子&#xff09;导读&#xff1a;工业级百兆超薄款网络变压器的生产要特殊的超薄磁芯配正确线径的铜线&#xff0c;使用符合相应防潮标准的凝固胶水。 一 ̖ 首先来看下商业级的超薄款的百兆网络变压器&#xff1a; 商业级&#xff08;消费级&…

Hadoop+Spark大数据技术 实验11 Spark 图

17周期末考试 重点从第五章 scala语言开始 比如&#xff1a;映射&#xff08;匿名函数&#xff09; 11.3.1创建属性图 import org.apache.spark.graphx._ import org.apache.spark.rdd.RDD //创建一个顶点集的RDD val users: RDD[(VertexId ,(String,String))] sc.paralle…

每日题库:Huawe数通HCIA——全部【813道】

1.关于ARP报文的说法错误的是?单选 A.ARP报文不能被转发到其他广播域 B.ARP应答报文是单播方发送的 C.任何链路层协议都需要ARP协议辅助获取数据链路层标识 DARP请求报文是广播发送的 答案:C  解析: STP协议不需要ARP辅助 2.园区网络搭建时,使用以下哪种协议可以避免出现二层…

MATLAB设计ATF教程

打开Control System Designer 在MATLAB命令行窗口输入sisotool 出现如下Control System Designer窗口 基础Compensator 打开工具后&#xff0c;Compensator初始为1&#xff0c;需要按照需求进行设计。本示例的传递函数为&#xff1a; 基于上述传递函数的Bode图进行后续的设计…

C语言scanf( ) 函数、fprintf( ) 函数与 scanf( ) 函数和printf( ) 函数有什么不同?

一、问题 fscanf( ) 函数、fprintf( ) 函数与 printf( ) 函数、scanf( ) 函数的作⽤相似&#xff0c;都是格式化读写函 数&#xff0c;那么这两个读写函数有什么不同呢&#xff1f; 二、解答 两者的区别就在于前⾯的字符“f”&#xff0c;即 fscanfQ函数和 fprintfD函数的读写…

新买的移动硬盘无法识别

文章目录 背景解决方案 背景 同事新买的移动硬盘&#xff0c;插在电脑上识别不出来盘符&#xff0c;检查了一下&#xff0c;硬盘没问题应该&#xff0c;是ssk的硬盘盒M.2的SSD&#xff0c;硬盘驱动也是正常的&#xff0c;插拔了几次&#xff0c;都不识别&#xff0c;换了太电脑…

​在哪些场景下,使用SOCKS5代理会特别有用?(socks5代理ip)​

SOCKS5代理作为网络协议转换的利器&#xff0c;其独特功能在众多实际场景中展现出了极大的价值。以下是几个特定场景&#xff0c;其中SOCKS5代理的使用将变得尤为重要&#xff1a; 一、网络安全与隐私访问 1.高级渗透测试&#xff1a;在网络安全领域&#xff0c;渗透测试人员…

Android 高德地图API(新版)

新版高德地图 前言正文一、创建应用① 获取PackageName② 获取调试版安全码SHA1③ 获取发布版安全码SHA1 二、配置项目① 导入SDK② 配置AndroidManifest.xml 三、获取当前定位信息① ViewBinding使用和导包② 隐私合规设置③ 权限请求④ 初始化定位⑤ 获取定位信息 四、显示地…

postgresql之翻页优化

列表和翻页是所有应用系统里面必不可少的需求&#xff0c;但是当深度翻页的时候&#xff0c;越深越慢。下面是几种常用方式 准备工作 CREATE UNLOGGED TABLE data (id bigint GENERATED ALWAYS AS IDENTITY,value double precision NOT NULL,created timestamp with time zon…

Django中使用Celery和APScheduler实现定时任务

在之前的文章我们已经学习了Celery和APScheduler的基本使用&#xff0c;下面让我们来了解一下如何在Django中使用Celery和APScheduler Celery 1.前提工作 python 3.7 pip install celery pip install eventlet #5.0版本以下 pip install importlib-metadata4.8.3&#xff08…

使用wheelnav.js构建酷炫的动态导航菜单

目录 前言 一、WheelNav是什么 1、项目地址 2、关于开源协议 3、相关目录介绍 二、如何使用wheelnav.js 1、新建html页面 2、设置style样式 3、创建展示元素实现动态导航 三、参数即方法介绍 1、参数列表 2、运行方法 3、实际成果 四、总结 前言 用户体验永远是一…

STM32项目分享:智能家居安防系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板及元器件图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.c…

PAT-1009 说反话(java实现)

还是这种题好&#xff0c;多简单啊&#xff0c;题目多清晰明了啊&#xff0c;多让人增加学习的热情啊。 题目 给定一句英语&#xff0c;要求你编写程序&#xff0c;将句中所有单词的顺序颠倒输出。 输入格式&#xff1a; 测试输入包含一个测试用例&#xff0c;在一行内给出总长…

安装vs2022低版本

https://learn.microsoft.com/en-us/visualstudio/releases/2022/release-history#updating-your-installation-to-a-specific-release 参考https://www.cnblogs.com/bodong/p/18084619

Android Kotlin 异步操作回调转换为挂起函数

异步接口回调是一种通过接口将任务的执行和结果处理分离开来的编程设计模式。通常用于网络请求、数据库查询等耗时操作。 挂起函数是 Kotlin 中的一个特性&#xff0c;用于简化异步编程。挂起函数是可以在协程中暂停执行并恢复的函数&#xff0c;避免了回调地狱问题&#xff0…

【Postman接口测试】第四节.Postman接口测试项目实战(中)

文章目录 前言五、Postman断言 5.1 Postman断言介绍 5.2 响应状态码断言 5.3 包含指定字符串断言 5.4 JSON数据断言六、参数化 5.1 Postman参数化介绍 5.2 Postman参数化实现 5.3 针对项目登录接口参数化实现 总结 前言 五、Postman断言 5.1 Postman断言介…

使用Aspose技术将Excel/Word转换为PDF

简介&#xff1a;本文将介绍如何使用Aspose技术将Excel文件转换为PDF格式。我们将使用Aspose-Cells-8.5.2.jar包&#xff0c;并演示Java代码以及进行测试。 一、Aspose技术概述 Aspose是一款强大的文档处理库&#xff0c;支持多种编程语言&#xff0c;如Java、C#、Python等。…