Skip to main content
16 面向对象编程

16 面向对象编程

•1 min•107 words

面向对象编程的几种特征:

数据+方法:一个对象同时封装了数据以及操作这些数据的过程

Rust的结构体与枚举,都包含数据和方法。
rust中的impl块中提供了方法和函数。

封装:一个对象的实现细节对使用该对象的代码不可见

我们可以使用 pub 关键字来决定代码中的哪些模块、类型、函数和方法是公有的,而默认情况下其他所有内容都是私有的。

继承:对象可以获得父对象的数据和行为,无需再次定义

如果一种语言必须拥有继承才能算作面向对象,那么 Rust 就不是这样的语言。Rust 没有办法在不借助宏的情况下,定义一个结构体去继承父结构体的字段和方法实现。

Todo

适用与老实现的代码,也能适用与新实现。这就是兼容的功效。“重用”的主要是集中控制+快速。

Rust使用trait机制来实现“重用”。

Rust使用泛型+trait约束来替代“多态”。

Cite
  1. 子类并不总是应该继承父类的全部特征,但使用继承时却往往会这样发生。
  2. 一些语言只支持单继承,这也进一步限制了程序设计的灵活性。

Trait Object 实现运行时多态

Summary

所以trait对象表示了结构体数据所以,以及该结构体的trait的所有方法所在。

trait 对象指向一个实现了我们指定 trait 的类型的实例,以及一个用于在运行时查找该类型的 trait 方法的表。

通过指定某种指针来创建 trait 对象,例如 & 引用或 Box<T> 智能指针,还有 dyn 关键字,以及指定相关的trait。

数据与方法解耦 6 泛型和特征

[!cite] 数据与方法解耦 6 泛型和特征
在 Rust 中,我们刻意不把结构体和枚举称为“对象”,以便把它们和其他语言里的对象区分开来。
在结构体或枚举里,字段中的数据和 impl 块里的行为是分开的,而在其他语言中,数据和行为往往会被合并进一个被称为对象的概念里。

Summary
通过虚表指针来寻找方法地址的技术叫作动态分发

单态化产生的代码在执行静态分发(static dispatch),也就是说编译器在编译时就知晓要调用什么方法。
这与动态分发 (dynamic dispatch)相对,这时编译器在编译时无法知晓要调用哪个方法。在动态分发的场景下,编译器会生成负责在运行时确定该调用什么方法的代码。

面向对象设计模式

状态模式:将随状态变化的数据与所需方法都打包成“状态对象”

函数式编程要求无状态,以保证可预测性。而对象的状态模式设计,则利用了状态,并且将每个状态所需的数据与方法打包成一个状态对象。这样整个世界都清静了。

状态转换方法:当然可以有多个

首先每个状态对象需要提供状态转换方法,返回新的状态对象。

通常需要消费老状态, 确保了当 状态转换为新状态后不能再使用老 state 值。状态转换 方法需要获取状态值的所有权。

所以状态对象字段都需要通过Option进行包装,调用 take 方法将 state 字段中的 Some 值取出并留下一个 None ,因为Rust 不允许结构体实例中存在未初始化的字段。