我的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

相关文章

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

MySQL双主搭建+keepalived高可用的实现

《MySQL双主搭建+keepalived高可用的实现》本文主要介绍了MySQL双主搭建+keepalived高可用的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,... 目录一、测试环境准备二、主从搭建1.创建复制用户2.创建复制关系3.开启复制,确认复制是否成功4.同

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

Java实现时间与字符串互相转换详解

《Java实现时间与字符串互相转换详解》这篇文章主要为大家详细介绍了Java中实现时间与字符串互相转换的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、日期格式化为字符串(一)使用预定义格式(二)自定义格式二、字符串解析为日期(一)解析ISO格式字符串(二)解析自定义

opencv图像处理之指纹验证的实现

《opencv图像处理之指纹验证的实现》本文主要介绍了opencv图像处理之指纹验证的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录一、简介二、具体案例实现1. 图像显示函数2. 指纹验证函数3. 主函数4、运行结果三、总结一、