十五、Rust 中的面向对象
1. OOP 基本特性
-
面向对象编程:Object-oriented Programing,简称OOP
-
面向对象三大特性:封装、继承、多态
-
封装
- 在 Rust 中,封装 体现在 trait、模块化、结构体、枚举
-
继承
- 在 Rust 中,没有体现 继承 的能力
- 在 Java 的传统设计模式中,有提到一个原则,叫做 “多用组合,少用继承”
- 在 Rust 中,组合 体现在 trait
-
多态
- 同一个行为具有多个不同表现形式或形态
- 在 Rust 中,多态 体现在 trait
-
一个练习
// 定义一个 trait, 用于指定可绘制元素的绘制方法 ==> 这类似于 Java 中的抽象类 (abstract class) 或者接口 (interface) pub trait Draw { fn draw(&self); } // 绘制器,包含了多个可绘制元素 ==> 这类似于 Java Class 中的属性 pub struct Screen { pub components: Vec<Box<dyn Draw>>, } // 为绘制器实现绘制方法 ==> 这类似于 Java Class 中的方法 impl Screen { pub fn run(&self) { for component in self.components.iter() { component.draw(); } println!("Screen drew ending."); } } // 一个具体的可绘制元素:按钮 ==> 这类似于 Java Class 中的属性 pub struct Button { pub width: i32, pub height: i32, pub label: String, } // 为这个可绘制元素提供绘制方法的实现 ==> 这类似于 Java Class 实现 (implements) 了指定的接口或继承 (extends) 了指定的抽象类 impl Draw for Button { fn draw(&self) { // 实现具体的内容 println!("... button drew ..."); } } pub fn oop_test() { let screen = Screen { components: vec![Box::new(Button { width: 32, height: 16, label: String::from("Button"), })], }; screen.run(); }
2. trait 特性
-
动态分发 (dynamic dispatch)
- 编译器在编译时无法知晓具体类型和对应的具体方法,只有在运行时才确定
-
静态分发 (static dispatch)
-
常发生在泛型具体化时,编译器会为每一个具体类型替代的泛型,生成非泛型的函数方法实现
// 例如如下的变量,它对应的泛型类型是 Vec<T>, // 在编译时,编译器会用 i32 来替代泛型 T,并为这个变量生成 i32 相关的函数和方法实现 let v = vec![1, 2, 3];
-
-
相比动态分发,静态分发会显得更高效一些
-
当使用 trait 对象时,Rust 必须使用动态分发,因为编译器无法知道实现了特定 trait 的所有类型
-
只有 对象安全(object safe)的 trait 才可以组成 trait 对象
- 如果一个 trait 的所有方法都满足如下条件,则这个 trait 是 对象安全 的
- 返回值类型不是
Self - 没有泛型类型的参数
- 返回值类型不是
- 如果一个 trait 的所有方法都满足如下条件,则这个 trait 是 对象安全 的
3. 设计模式之:状态模式
-
状态模式:一个对象暴露出了一个或多个值,且具有一些内部状态,暴露出的值会随着这些内部状态的变化而变化
-
优点:当业务需求改变时,无需改变值持有状态或者使用值的代码;需要暴露新的值时,也可直接扩展
-
缺点:状态之间可能会相互联系,导致一些逻辑的紧耦合;可能会有一些重复逻辑无法复用
-
一个示例
// 根据官方教程的一个示例改编的示例 // #region lib.rs trait State { // Box<Self>: 这个方法调用只对这个类型的 Box 有效,与 self、 &self 或者 &mut self 完全不同 /// 从当前状态切换到下一步的状态 fn apply_for_next_state(self: Box<Self>) -> Box<dyn State>; /// 从当前状态切换回上一步的状态 fn reject_to_last_state(self: Box<Self>) -> Box<dyn State>; /// 打印自己 fn show(&self); /// 获取文章的内容 fn content<'a>(&self, _post: &'a Post) -> &'a str { "" } } // 草稿 struct Draft {} impl State for Draft { // 草稿状态 申请 之后就会进入 待审核状态 fn apply_for_next_state(self: Box<Self>) -> Box<dyn State> { return Box::new(PendingReview {}); } // 草稿状态 回退 之后还是 草稿状态 fn reject_to_last_state(self: Box<Self>) -> Box<dyn State> { return self; } fn show(&self) { println!("Draft"); } } // 待审核 struct PendingReview {} impl State for PendingReview { // 待审核状态 申请 之后就会进入 已发布状态 fn apply_for_next_state(self: Box<Self>) -> Box<dyn State> { return Box::new(Published {}); } // 待审核状态 回退 之后就会进入 草稿状态 fn reject_to_last_state(self: Box<Self>) -> Box<dyn State> { return Box::new(Draft {}); } fn show(&self) { println!("PendingReview"); } } // 已发布 struct Published {} impl State for Published { // 已发布状态 申请 之后还是 已发布状态 fn apply_for_next_state(self: Box<Self>) -> Box<dyn State> { return self; } // 已发布状态 回退 之后就会进入 待审核状态 fn reject_to_last_state(self: Box<Self>) -> Box<dyn State> { return Box::new(PendingReview {}); } fn show(&self) { println!("Published"); } fn content<'a>(&self, post: &'a Post) -> &'a str { return &post.content; } } 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_content(&mut self, appending: &str) { self.content.push_str(appending); } /// 获取文章当前的内容 pub fn content(&self) -> &str { return self.state.as_ref().unwrap().content(self); } /// 提交审核 pub fn request_review(&mut self) { // take: 将 state 字段中的 Some 值取出并留下一个 None if let Some(s) = self.state.take() { self.state = Some(s.apply_for_next_state()); } } /// 审核通过 pub fn approve(&mut self) { if let Some(s) = self.state.take() { self.state = Some(s.apply_for_next_state()); } } /// 查看文章当前的状态 pub fn show_state(&self) { let state = self.state.as_ref(); match state { Some(s) => { print!("Now, the state of current post is: "); s.show(); } None => println!("Now, the state of current post is None."), }; } } // #endregion // #region main.rs use blog::Post; fn main () { let mut post = Post::new(); post.add_content("This is the first part."); post.show_state(); println!("The content of post is: {}", post.content()); post.request_review(); post.show_state(); println!("The content of post is: {}", post.content()); post.approve(); post.show_state(); println!("The content of post is: {}", post.content()); } // #endregion -
将上面通过 OOP 状态模式实现的代码,改为 Rust 类型
// 来自官方教程的一个示例 // #region lib.rs pub struct DraftPost { content: String, } impl DraftPost { pub fn add_content(&mut self, content: &str) { self.content.push_str(content); } pub fn request_review(self) -> PendingReviewPost { return PendingReviewPost { content: self.content, }; } } pub struct PendingReviewPost { content: String, } impl PendingReviewPost { pub fn approve(self) -> Post { return Post { content: self.content, }; } } pub struct Post { content: String, } impl Post { pub fn new() -> DraftPost { return DraftPost { content: String::new(), }; } pub fn content(&self) -> &str { return &self.content; } } // #endregion // #region main.rs use blog::Post; fn main() { let mut post = Post::new(); post.add_content("This is the first part of content."); // println!("The content of post is: {}", post.content); // 访问不到,因为 content 是私有的 let post = post.request_review(); // println!("The content of post is: {}", post.content); // 访问不到,因为 content 是私有的 let post = post.approve(); println!("The content of post is: {}", post.content()); } // #endregion