我的RUST学习—— 【第十七章 17-3】面向对象设计模式的实现

2024-02-29 10:08

本文主要是介绍我的RUST学习—— 【第十七章 17-3】面向对象设计模式的实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这一节啊,其实很有启发。

整个这一章都在讲Rust与面向对象编程,但是这一节反而告诉你,不要被思维惯式禁锢了你的想法。

我们在熟悉了面向对象编程之后,自然而然的会使用类、对象、继承、多态等方案来幻想之后的编程。但是如今我们已经在学习一门新语言了,完全可以利用新语言的优势——所有权等来构筑新的思考方式。

但是作者还是很贴心的先使用面向对象的思维,使用继承这一套手段结合Rust为我们实现了一个 博客发布过程 的小例子。当我们惊叹其中手法的巧妙之时,作者突然笔锋一转,告诉你如果使用Rust的特性来实现这个例子,会在完成所有功能的情况下,更加完美的实现整个例子。

铺垫了这么多直接来看代码思路。

使用面向对象来实现博客发布过程

先做需求分析,一个博客有三个状态:

  • 草稿,只能添加内容,无法展示内容,只能送审
  • 已审核,此时草稿已审核完毕,此时无法添加内容,也无法展示内容,只能继续发布
  • 已发布,此时是正式文档,不能添加内容,可以展示内容

这个过程可以用一个有限状态机表示。

如果用传统的面向对象思考,很容易得出初步的解决方案,有一个文章类,它上面有很多方法

  • new,创建一个文章
  • add_text,添加一些内容
  • content,展示内容
  • request_review,送审
  • approve,发布

并且,这个文章类有两个成员:

  • 状态
  • 内容

然后创建子类继承于文章类:

  • Draft,草稿
  • PendingReview,审核中
  • Published,已发布

那么这三个子类都要灵活的、或多或少实现,文章类的五个方法,只不过在不同状态下,具体逻辑不一样,比如草稿类调用送审,返回一个审核中类,但是审核中类送审,返回自身,因为它其实无法调用送审这个方法。

具体代码如下,或者去官网看一步步实现的逻辑,这里不赘述:

// lib.rs
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(),}}pub fn add_text (&mut self, text: &str) {self.content.push_str(text);}// pub fn content (&self) -> &str {//     ""// }pub fn content (&self) -> &str {self.state.as_ref().unwrap().content(self)}pub fn request_review(&mut self) {if let Some(s) = self.state.take() {self.state = Some(s.request_review());}}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>;fn content<'a> (&self, post: &'a Post) -> &'a str {""}
}struct Draft {}impl State for Draft {fn request_review(self: Box<Self>) -> Box<dyn State> {Box::new(PendingReview {})}fn approve(self: Box<Self>) -> Box<dyn State> {self}
}struct PendingReview {}impl State for PendingReview {fn request_review(self: Box<Self>) -> Box<dyn State> {self}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}fn content<'a> (&self, post: &'a Post) -> &'a str {&post.content}
}// main.rs
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());
}

其实如果你看懂了上面的实现代码,你会发现这和面向对象还是有一些小区别,因为这毕竟是Rust,没有原生支持各种面向对象操作。

我们这里是用状态模式,这一设计模式来实现面向对象。具体概念见官网文档。

将状态编码为不同类型

我们使用Rust的一些东西,来实现这一过程。

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

我们完全可以只用三种类型来代表,文章的三个状态:

  • Post,已发布
  • PendingReviewPost,审核中
  • DraftPost,草稿

这样,只为这三种不同的类型实现各自的方法,这样,就能保证,当处于某一类型下,只能使用自己的方法。当你处于其他类型下时,是无法调用其他类型的方法的,这也借用了Rust强大的类型检查机制。比如,如果一个文章已发布,那么只能调用,查看内容 这一个方法。

以下是代码:

// lib.rs
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);}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}}
}
// main.rs
use blog::Post;
fn main() {let mut post = Post::new();post.add_text("I ate a salad for lunch today");// assert_eq!("", post.content()); 已无法调用let post = post.request_review();// assert_eq!("", post.content()); 已无法调用let post = post.approve();assert_eq!("I ate a salad for lunch today", post.content());
}

小节总结

其实你会发现这两种方案,对应的 main.rs中的内容很不同,前者的main.rs是很经典的面向对象写法,后者的你压根不能随便调用方法,因为类型不匹配会直接报错。

如果你是高手的话,你其实会发现,不管是哪种写法根本是无所谓的,不管是面向过程,还是面向对象都是一种思维,一种思考方式。你完全可以用Rust 模拟继承、封装、多态,也完全可以用纯面向过程的思路,完成第二种方式的代码,这只取决于你个人。

总结

阅读本章后,不管你是否认为 Rust 是一个面向对象语言,现在你都见识了 trait 对象是一个 Rust 中获取部分面向对象功能的方法。动态分发可以通过牺牲少量运行时性能来为你的代码提供一些灵活性。这些灵活性可以用来实现有助于代码可维护性的面向对象模式。Rust 也有像所有权这样不同于面向对象语言的功能。面向对象模式并不总是利用 Rust 优势的最好方式,但也是可用的选项。

接下来,让我们看看另一个提供了多样灵活性的 Rust 功能:模式。贯穿全书的模式, 我们已经和它们打过照面了,但并没有见识过它们的全部本领。让我们开始探索吧!

这篇关于我的RUST学习—— 【第十七章 17-3】面向对象设计模式的实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/758401

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

在JS中的设计模式的单例模式、策略模式、代理模式、原型模式浅讲

1. 单例模式(Singleton Pattern) 确保一个类只有一个实例,并提供一个全局访问点。 示例代码: class Singleton {constructor() {if (Singleton.instance) {return Singleton.instance;}Singleton.instance = this;this.data = [];}addData(value)