面向对象语言的特征
编程社区对于具有 语言必须被视为面向对象的。Rust 受到许多 编程范式,包括 OOP;例如,我们探索了 这来自第 13 章的函数式编程。可以说,OOP 语言 共享某些共同特征,即 objects、encapsulation 和 遗产。让我们看看这些特征中的每一个意味着什么,以及 Rust 支持它。
对象包含数据和行为
《设计模式:可重用面向对象的软件的元素》一书 作者 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides (Addison-Wesley Professional, 1994),俗称 The Gang of Four book,是一本 面向对象的设计模式目录。它以这种方式定义 OOP:
面向对象的程序由对象组成。对象将两者打包 data 和对该数据进行作的过程。这些过程是 通常称为方法或作。
使用这个定义,Rust 是面向对象的:结构和枚举有数据,
块提供 struct 和 enum 的方法。即使 structs 和
带有方法的枚举不称为对象,它们提供相同的
功能,根据 Gang of Four 对对象的定义。impl
隐藏实现细节的封装
通常与 OOP 相关的另一个方面是封装的思想, 这意味着 Object 的实现细节不能被 代码。因此,与对象交互的唯一方法是 通过其公共 API;使用该对象的代码不应能够进入 对象的内部结构,并直接更改数据或行为。这将启用 程序员来更改和重构对象的内部结构,而无需 更改使用该对象的代码。
我们在第 7 章中讨论了如何控制封装:我们可以使用关键字来决定代码中的哪些模块、类型、函数和方法
应该是 public 的,默认情况下,其他所有内容都是私有的。例如,我们
可以定义一个结构体,该结构体具有一个包含向量的字段
的值。结构还可以具有一个字段,其中包含
向量中的值,这意味着不必计算平均值
在任何人需要时按需提供。换句话说,将
为 us 缓存计算出的平均值。示例 17-1 有结构体的定义:pub
AveragedCollection
i32
AveragedCollection
AveragedCollection
文件名: src/lib.rs
pub struct AveragedCollection {
list: Vec<i32>,
average: f64,
}
该结构被标记,以便其他代码可以使用它,但
结构保持私有。在这种情况下,这很重要,因为我们希望
确保无论何时在列表中添加或删除值,平均值为
也更新了。我们通过实现 、 和 方法来做到这一点
在结构体上,如示例 17-2 所示:pub
add
remove
average
文件名: src/lib.rs
pub struct AveragedCollection {
list: Vec<i32>,
average: f64,
}
impl AveragedCollection {
pub fn add(&mut self, value: i32) {
self.list.push(value);
self.update_average();
}
pub fn remove(&mut self) -> Option<i32> {
let result = self.list.pop();
match result {
Some(value) => {
self.update_average();
Some(value)
}
None => None,
}
}
pub fn average(&self) -> f64 {
self.average
}
fn update_average(&mut self) {
let total: i32 = self.list.iter().sum();
self.average = total as f64 / self.list.len() as f64;
}
}
公共方法 、 和 是访问
或修改 实例中的数据。添加项时
添加到使用该方法或删除的
每个 Alpha 的实现都会调用处理
同时更新字段。add
remove
average
AveragedCollection
list
add
remove
update_average
average
我们将 and 字段保留为私有,因此没有办法
用于直接在字段中添加或删除项目的外部代码;
否则,字段可能会在更改时变得不同步。该方法返回字段中的值
允许外部代码读取但不能修改它。list
average
list
average
list
average
average
average
因为我们已经封装了 结构体的实现细节 ,我们可以很容易地改变方面,比如数据结构、
在未来。例如,我们可以对字段使用 a 而不是 a。只要 、 和 public 方法的签名保持不变,则无需更改代码使用即可进行编译。如果我们改为 public,则不一定是这种情况:并且具有不同的添加和删除项目的方法,因此外部的
如果直接修改代码,则可能必须更改代码。AveragedCollection
HashSet<i32>
Vec<i32>
list
add
remove
average
AveragedCollection
list
HashSet<i32>
Vec<i32>
list
如果封装是要考虑的语言的必要方面
面向对象,那么 Rust 就满足了这个要求。使用 或
not for different parts of code 启用实现详细信息的封装。pub
作为类型系统和代码共享的继承
继承是一种机制,通过这种机制,对象可以从 另一个对象的定义,从而获得父对象的数据和行为 而无需再次定义它们。
如果语言必须具有继承才能成为面向对象的语言,则 Rust 不是其中之一。无法定义继承父级的结构体 struct 的字段和方法实现,而无需使用宏。
但是,如果您习惯于在编程工具箱中使用继承,则可以 可以使用 Rust 中的其他解决方案,具体取决于您选择 继承。
选择继承有两个主要原因。一个用于代码重用:
您可以为一种类型实现特定行为,而继承使您能够
将该实现重新用于不同类型的实现。您可以在有限的
way 在 Rust 代码中使用默认 trait 方法实现,你在
示例 10-14 当我们添加该方法的默认实现时
在性状上。任何实现 trait 的类型都会有
该方法可用,无需任何其他代码。这是
类似于具有 Method 和
inheriting child class 也具有该方法的实现。我们可以
也覆盖该方法的默认实现
实现 trait,这类似于 child 类覆盖
从父类继承的方法的实现。summarize
Summary
Summary
summarize
summarize
Summary
使用继承的另一个原因与类型系统有关:要启用 child 类型,以便在与 parent 类型相同的位置使用。这也是 称为多态性,这意味着您可以将多个对象替换为 如果它们具有某些特征,则它们在运行时相互关联。
多态性
对许多人来说,多态性是继承的同义词。但事实是 实际上是一个更通用的概念,指的是可以处理数据的代码 多种类型。对于继承,这些类型通常是 subclass。
相反,Rust 使用泛型来抽象不同的可能类型和 trait 边界来对这些类型必须提供的内容施加约束。这是 有时称为有界参数多态性。
继承作为一种编程设计解决方案最近已经失宠 在许多编程语言中,因为它通常面临共享更多代码的风险 比必要。子类不应该总是共享其 parent 类,但会通过继承来执行此作。这可以使程序的设计 不太灵活。它还引入了在 没有意义的子类,或者因为方法没有意义而导致错误的子类 apply 应用于子类。此外,某些语言将只允许单个 inheritance (意味着一个子类只能继承自一个类),更进一步 限制程序设计的灵活性。
由于这些原因,Rust 采用了不同的方法来使用 trait 对象 而不是继承。让我们看看 trait 对象如何在 锈。
本文档由官方文档翻译而来,如有差异请以官方英文文档(https://doc.rust-lang.org/)为准