18.1 面向对象语言的特征
18.1.1 对象:数据 + 行为

18.1.2 封装隐藏了实现细节
- 在 Rust 中,在代码中不同的部分考虑使用
pub可以封装其实现细节


18.1.3 继承,作为类型系统与代码共享
- 在 Rust 中,不存在继承的机制,而是使用 trait 对象来提供相关的功能

18.2 trait 对象
- 已知全部的数据类型时,可以使用固定集合(枚举);但当全部的数据类型不是已知时(会动态变化),就需要使用 trait 对象来实现了

18.2.1 trait 对象(是一个实例):定义通用行为
trait对象:指向一个实现了指定trait类型的实例,以及一个用于在运行时查找该类型的trait方法的表- 通过指定某种**指针(
&引用或Box<T>智能指针,还有dyn关键字)**来创建trait对象 - 可以使用
trait对象代替泛型或具体类型,无需在编译时就知晓所有可能的类型 trait对象将数据和行为两者相结合(不同于传统的对象,不能向trait对象增加数据),trait对象的作用是允许对通用行为进行抽象- 对比泛型类型:泛型类型参数一次只能替代一个具体类型,而
trait对象则允许在运行时替代多种具体类型


18.2.2 实现 trait
- 将一个或多个类型的实例,放入
Box<T>中就可以转换得到trait对象! - 使用
trait对象和 Rust 类型系统来进行类似鸭子类型操作的优势是无需在运行时检查一个值是否实现了特定方法或者担心在调用时因为值没有实现方法而产生错误。如果值没有实现trait对象所需的trait则 Rust 不会编译这些代码



18.2.3 trait 对象执行动态分发

18.2.4 trait 对象需要类型安全
- 只有**对象安全(object-safe)**的 trait 可以实现为特征对象
- 如果一个 trait 中定义的所有方法都符合以下规则,则该 trait 是对象安全的:1、返回值不是 Self;2、没有泛型类型的参数
- 核心原因:一旦使用 trait 对象,Rust 将不再知晓该实现的返回类型!

18.3 面向对象设计模式的实现
18.3.1 状态模式
- 状态模式(state pattern):是一个面向对象设计模式,该模式的关键在于定义一系列值的内含状态,这些状态体现为一系列的状态对象,同时值的行为随着其内部状态而改变
- 状态对象共享功能
- 每一个状态对象负责其自身的行为,以及该状态何时应当转移至另一个状态。而持有一个状态对象的值,对于不同状态的行为以及何时状态转移毫不知情


18.3.2 博客项目(一):定义 Post 并新建一个草案状态的实例

18.3.3 博客项目(二):存放博文内容的文本
- 对于需要修改的字段需要传入可变引用
&mut self

18.3.4 博客项目(三):确保博文草案的内容是空的

18.3.5 博客项目(四):请求审核博文来改变其状态
- 代码解析:
if let Some(s) = self.state.take()表示先取出当前state的值及其所有权,再给到中间变量s,s再根据实际获取到的state值调用对应的request_review方法(即判断是属于Draft还是PendingReview的request_review) - 在这里来看看状态模式的优势:无论 state 是何值,Post 的 request_review 方法都是一样的。每个状态只负责它自己的规则


18.3.6 博客项目(五):增加改变 content 行为的 approve 方法
- 调用
Option的as_ref方法是因为需要Option中值的引用而不是获取其所有权 - 在下面的示例中,获取到的
&Box<dyn State>,当调用其 content 时,Deref 强制转换会作用于&和Box - 在下面的示例中,获取 post 的引用作为参数,并返回 post 一部分的引用,所以返回的引用的生命周期与 post 参数相关



18.3.7 状态模式的权衡取舍
- 状态模式的优点:Post 的方法和使用 Post 的位置无需
match语句,同时增加新状态只涉及到增加一个新struct和为其实现trait的方法 - 状态模式的缺点:因为状态实现了状态之间的转换,一些状态会相互联系;会有一些重复的逻辑(可通过默认方法、定义宏等方法来解决)

18.3.8 博客项目-改造(一):将状态和行为编码为类型


18.3.9 博客项目-改造(二):实现状态转移为不同类型的转换


18.4 小结


















