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

相关文章

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依

利用python实现对excel文件进行加密

《利用python实现对excel文件进行加密》由于文件内容的私密性,需要对Excel文件进行加密,保护文件以免给第三方看到,本文将以Python语言为例,和大家讲讲如何对Excel文件进行加密,感兴... 目录前言方法一:使用pywin32库(仅限Windows)方法二:使用msoffcrypto-too

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

springboot使用Scheduling实现动态增删启停定时任务教程

《springboot使用Scheduling实现动态增删启停定时任务教程》:本文主要介绍springboot使用Scheduling实现动态增删启停定时任务教程,具有很好的参考价值,希望对大家有... 目录1、配置定时任务需要的线程池2、创建ScheduledFuture的包装类3、注册定时任务,增加、删

SpringBoot整合mybatisPlus实现批量插入并获取ID详解

《SpringBoot整合mybatisPlus实现批量插入并获取ID详解》这篇文章主要为大家详细介绍了SpringBoot如何整合mybatisPlus实现批量插入并获取ID,文中的示例代码讲解详细... 目录【1】saveBATch(一万条数据总耗时:2478ms)【2】集合方式foreach(一万条数

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1