mirror of
https://github.com/gnu4cn/rust-lang-zh_CN.git
synced 2026-08-19 12:43:28 +08:00
Updated 'src/oop/trait_objects.md'.
This commit is contained in:
@@ -185,7 +185,7 @@ error: could not compile `vector_demo` (bin "vector_demo") due to 1 previous err
|
||||
|
||||
例如,假设我们想要获取电子表格某行中的值,其中该行中某些列包含整数、浮点数与字符串。我们可以定义一个枚举,其变种将保存不同的值类型,而所有枚举变种都将被视为同一类型:即这个枚举。然后,我们可以创建一个矢量来保存这个枚举,进而最终保存不同类型。我们已在下面清单 8-9 中演示了这点。
|
||||
|
||||
<a name="listing_8-8"></a>
|
||||
<a name="listing_8-9"></a>
|
||||
```rust
|
||||
enum SpreadsheetCell {
|
||||
Int(i32),
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
|
||||
Erich Gamma、Richard Helm、Ralph Johnson 及 John Vlissides 等的合著 *[Design Patterns: Elements of Reusable Object-Oriented Software](https://en.wikipedia.org/wiki/Design_Patterns)* (Addison-Wesley Professional, 1994),又被通俗地叫做 *The Gang of Four* 书,是面向对象设计模式的汇编。该书是这样定义 OOP 的:
|
||||
|
||||
> “面向对象的程序对象所组成。**对象** 封装了数据和操作该数据的过程。这些过程通常称为 **方法** 或 **操作**”。
|
||||
> “
|
||||
> 面向对象的程序对象所组成。**对象** 封装了数据和操作该数据的过程。这些过程通常称为 **方法** 或 **操作**
|
||||
> ”。
|
||||
|
||||
使用这个定义,那么 Rust 是面向对象的:结构体和枚举包含数据,而 `impl` 代码块提供结构体和枚举上的方法。尽管带有方法的结构体与枚举未 *被称作* 对象,但根据 The Gang of Four 对对象的定义,他们提供了同样的功能。
|
||||
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
# 使用特质对象抽象共用行为
|
||||
# 使用特质对象来抽象共用行为
|
||||
|
||||
> **注**:这类似于 Java 语言中,解决死亡钻石问题(DDD)的 [接口](https://java.xfoss.com/Ch08_Interfaces_and_Abstract_Classes.html#%E4%BD%BF%E7%94%A8%E6%8E%A5%E5%8F%A3%E6%9D%A5%E6%8B%AF%E6%95%91)。
|
||||
> **译注**:这类似于 Java 语言中,解决死亡钻石问题(DDD)的 [接口](https://java.xfoss.com/Ch08_Interfaces_and_Abstract_Classes.html#%E4%BD%BF%E7%94%A8%E6%8E%A5%E5%8F%A3%E6%9D%A5%E6%8B%AF%E6%95%91)。
|
||||
|
||||
在第 8 章中,咱们就提到过矢量值的一个局限,便是他们只能存储一种类型的元素。在清单 8-9 中咱们创建出了一种变通方案,其中定义了有着分别保存整数、浮点数与文本变种的 `SpreadsheetCell` 枚举。这就意味着咱们可在各个单元格中存储不同类型的数据,而仍旧有了表示这些单元格所组成行的一个矢量值。这对于在咱们的代码被编译时,就已经清楚这些可交换项目,为固定类型集的情况,这确实是一种相当不错的解决办法。
|
||||
在第 8 章中,我们提到矢量值的一个限制是,他们只能存储一种类型的元素。我们在 [清单 8-9](../common_collections/vectors.md#listing_8-9) 中创建了一种变通方案,其中定义了一个 `SpreadsheetCell` 枚举,有着分别保存整数、浮点数与文本的变种。这就意味着我们可以在每个单元格中存储不同类型的数据,并且仍然有个表示一行单元格的矢量值。当我们的可互换项在代码编译时就属于已知的固定类型时,这是一种非常好的解决方案。
|
||||
|
||||
然而,有时咱们会想要咱们库的用户,能够扩展这个于某种特定情形下有效的类型集。为展示咱们将怎样达成这个目的,接下来咱们将创建对一个条目清单加以迭代的示例性图形用户界面,graphical user interface,GUI 工具 -- 对于 GUI 工具来讲这可是一项常见技能。咱们将创建包含 GUI 库架构的名为 `gui` 的一个库代码箱。此代码箱会包含给人类使用的一些类型,比如 `Button` 或 `TextField`。此外,`gui` 的用户将希望创建出他们自己的能被绘制出来的类型:比如,某个程序员要添加一个 `Image`,而另一程序员则要添加一个 `SelectBox`。
|
||||
然而,有时我们希望库的用户,能够扩展这一在特定情形下有效的类型集。为了展示我们如何实现这一点,我们将创建一个示例图形用户界面(GUI)工具,其将遍历项目列表,对每个项目调用 `draw` 方法来绘制项目到屏幕 -- 这属于 GUI 工具的常见技术。我们将创建一个名为 `gui` 的库,包含 GUI 库的架构。这个代码箱可能包含一些供人们使用的类型,例如 `Button` 或 `TextField`。此外,`gui` 的用户将希望创建自己的可绘制类型:例如,某名程序员可能会添加一个 `Image`,而另一程序员可能会添加一个 `SelectBox`。
|
||||
|
||||
对于这个示例,咱们不会实现一个完全成熟的 GUI 库,而是会给出这些部分将怎样一起配合起来。在编写这个库时,咱们没法了解而定义出其他那些程序员可能想要创建的全部类型。但咱们肯定清楚 `gui` 需要追踪各种不同类型的许多不同值,同时他还需要调用这些不同类型值上的 `draw` 方法。其无需明白在咱们调用该 `draw` 方法时,具体会发生什么,他只需知道那个值会让那个方法可被咱们调用。
|
||||
在编写这个库时,我们无法知道并定义其他程序员可能想要创建的所有类型。但我们确实清楚 `gui` 需要追踪许多不同类型的值,并且他需要对每个这些不同类型的值都调用 `draw` 方法。他无需确切知道调用 `draw` 方法时将发生什么,只需知道值将具有可供我们调用的方法即可。
|
||||
|
||||
在有着继承的某门语言中要做到这点,咱们可能会定义其上有着名为 `draw` 的方法的一个名为 `Component` 类。至于其他类,比如 `Button`、`Image` 与 `SelectBox` 等,将从 `Component` 基础并因此继承这个 `draw` 方法。他们可以分别重写这个 `draw` 方法,来定义他们的定制行为,而框架则可以将全部这些类型,当作 `Component` 的实例对待而调用他们之上的 `draw`。但由于 Rust 并无继承,因此咱们需要另一种方法,来架构这个 `gui` 库,来允许用户以新类型来扩展他。
|
||||
要在有着继承的语言中实现这一点,我们可以定义一个名为 `Component` 类,有着名为 `draw` 的方法。其他类,比如 `Button`、`Image` 与 `SelectBox` 等,将继承自 `Component`,从而继承 `draw` 方法。他们每个都可以重写 `draw` 方法,来定义他们的定制行为,而框架可以将所有类型都视为 `Component` 的实例,并对他们调用 `draw` 方法。但由于 Rust 没有继承,我们需要另一种方式来架构 `gui` 库,以允许用户创建与该库兼容的新类型。
|
||||
|
||||
|
||||
## 定义用于共同行为的特质
|
||||
|
||||
**Defining a Trait for Common Behavior**
|
||||
|
||||
## 为共同行为定义特质
|
||||
|
||||
为了实现咱们想要 `gui` 所拥有的行为,咱们将定义将有着一个名为 `draw` 方法的名为 `Draw` 特质。随后咱们就可以定义取 *特质对象,a trait object* 的一个矢量。特质对象会同时指向实现了这个指定特质的某个类型,以及用于在运行时查找那个类型上特质方法的一张表。咱们是通过指定某种指针,比如某个 `&` 的引用,或某个 `Box<T>` 的灵巧指针,接着便是 `dyn` 关键字,以及随后指明相关特质,创建出特质对象。(在第 19 章的 [“动态大小类型与 `Sized` 特质”](Ch19_Advanced_Features.md#动态大小的类型与-sized-特质) 小节咱们将讲到特质对象必须使用指针的原因。)在泛型或具体类型处,咱们就可以使用特质对象。而不论在何处使用特质对象,Rust 的类型系统都会确保在编译时,在那样的上下文中的任何值,都将实现该特质对象的特质。于是,咱们就无需掌握编译时的所有可能类型了。
|
||||
|
||||
|
||||
Reference in New Issue
Block a user