我的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和OpenCV库实现实时颜色识别系统

《使用Python和OpenCV库实现实时颜色识别系统》:本文主要介绍使用Python和OpenCV库实现的实时颜色识别系统,这个系统能够通过摄像头捕捉视频流,并在视频中指定区域内识别主要颜色(红... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间详解

PostgreSQL中MVCC 机制的实现

《PostgreSQL中MVCC机制的实现》本文主要介绍了PostgreSQL中MVCC机制的实现,通过多版本数据存储、快照隔离和事务ID管理实现高并发读写,具有一定的参考价值,感兴趣的可以了解一下... 目录一 MVCC 基本原理python1.1 MVCC 核心概念1.2 与传统锁机制对比二 Postg

SpringBoot整合Flowable实现工作流的详细流程

《SpringBoot整合Flowable实现工作流的详细流程》Flowable是一个使用Java编写的轻量级业务流程引擎,Flowable流程引擎可用于部署BPMN2.0流程定义,创建这些流程定义的... 目录1、流程引擎介绍2、创建项目3、画流程图4、开发接口4.1 Java 类梳理4.2 查看流程图4

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

C++高效内存池实现减少动态分配开销的解决方案

《C++高效内存池实现减少动态分配开销的解决方案》C++动态内存分配存在系统调用开销、碎片化和锁竞争等性能问题,内存池通过预分配、分块管理和缓存复用解决这些问题,下面就来了解一下... 目录一、C++内存分配的性能挑战二、内存池技术的核心原理三、主流内存池实现:TCMalloc与Jemalloc1. TCM

OpenCV实现实时颜色检测的示例

《OpenCV实现实时颜色检测的示例》本文主要介绍了OpenCV实现实时颜色检测的示例,通过HSV色彩空间转换和色调范围判断实现红黄绿蓝颜色检测,包含视频捕捉、区域标记、颜色分析等功能,具有一定的参考... 目录一、引言二、系统概述三、代码解析1. 导入库2. 颜色识别函数3. 主程序循环四、HSV色彩空间

Python实现精准提取 PDF中的文本,表格与图片

《Python实现精准提取PDF中的文本,表格与图片》在实际的系统开发中,处理PDF文件不仅限于读取整页文本,还有提取文档中的表格数据,图片或特定区域的内容,下面我们来看看如何使用Python实... 目录安装 python 库提取 PDF 文本内容:获取整页文本与指定区域内容获取页面上的所有文本内容获取

基于Python实现一个Windows Tree命令工具

《基于Python实现一个WindowsTree命令工具》今天想要在Windows平台的CMD命令终端窗口中使用像Linux下的tree命令,打印一下目录结构层级树,然而还真有tree命令,但是发现... 目录引言实现代码使用说明可用选项示例用法功能特点添加到环境变量方法一:创建批处理文件并添加到PATH1

Java使用HttpClient实现图片下载与本地保存功能

《Java使用HttpClient实现图片下载与本地保存功能》在当今数字化时代,网络资源的获取与处理已成为软件开发中的常见需求,其中,图片作为网络上最常见的资源之一,其下载与保存功能在许多应用场景中都... 目录引言一、Apache HttpClient简介二、技术栈与环境准备三、实现图片下载与保存功能1.

canal实现mysql数据同步的详细过程

《canal实现mysql数据同步的详细过程》:本文主要介绍canal实现mysql数据同步的详细过程,本文通过实例图文相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的... 目录1、canal下载2、mysql同步用户创建和授权3、canal admin安装和启动4、canal