Update ch17-03-oo-design-patterns.md

This commit is contained in:
庄秋彬 2020-02-25 00:04:55 +08:00 committed by GitHub
parent e24c834654
commit db85604b64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -282,11 +282,11 @@ impl Post {
因为目标是将所有像这样的规则保持在实现了 `State` 的结构体中,我们将调用 `state` 中的值的 `content` 方法并传递博文实例(也就是 `self`)作为参数。接着返回 `state` 值的 `content` 方法的返回值。
这里调用 `Option``as_ref` 方法是因为需要 `Option` 中值的引用而不是获取其所有权。因为 `state` 是一个 `Option<Box<State>>`,调用 `as_ref` 会返回一个 `Option<&Box<State>>`。如果不调用 `as_ref`会得到一个错误,因为不能将 `state` 移动出借用的 `&self` 函数参数。
这里调用 `Option``as_ref` 方法是因为需要 `Option` 中值的引用而不是获取其所有权。因为 `state` 是一个 `Option<Box<State>>`,调用 `as_ref` 会返回一个 `Option<&Box<State>>`。如果不调用 `as_ref`会得到一个错误,因为不能将 `state` 移动出借用的 `&self` 函数参数。
接着调用 `unwrap` 方法,这里我们知道它永远也不会 panic因为 `Post` 的所有方法都确保在他们返回时 `state` 会有一个 `Some` 值。这就是一个第十二章 [“当我们比编译器知道更多的情况”][more-info-than-rustc] 部分讨论过的我们知道 `None` 是不可能的而编译器却不能理解的情况。
接着我们就有了一个 `&Box<State>`,当调用其 `content` 时,解引用强制多态会作用于 `&``Box` 这样最终会调用实现了 `State` trait 的类型的 `content` 方法。这意味着需要为 `State` trait 定义增加 `content`,这也是放置根据所处状态返回什么内容的逻辑的地方,如示例 17-18 所示:
接着我们就有了一个 `&Box<State>`,当调用其 `content` 时,解引用强制多态会作用于 `&``Box` 这样最终会调用实现了 `State` trait 的类型的 `content` 方法。这意味着需要为 `State` trait 定义增加 `content`,这也是放置根据所处状态返回什么内容的逻辑的地方,如示例 17-18 所示:
<span class="filename">文件名: src/lib.rs</span>
@ -322,7 +322,7 @@ impl State for Published {
### 状态模式的权衡取舍
我们展示了 Rust 是能够实现面向对象的状态模式的,以便能根据博文所处的状态来封装不同类型的行为。`Post` 的方法并不知道这些不同类型的行为。通过这种组织代码的方式,为了找到所有已发布的博文不同行为只需查看一处代码:`Published` 的 `State` trait 的实现。
我们展示了 Rust 是能够实现面向对象的状态模式的,以便能根据博文所处的状态来封装不同类型的行为。`Post` 的方法并不知道这些不同类型的行为。通过这种组织代码的方式,要找到所有已发布博文的不同行为只需查看一处代码:`Published` 的 `State` trait 的实现。
如果要创建一个不使用状态模式的替代实现,则可能会在 `Post` 的方法中,或者甚至于在 `main` 代码中用到 `match` 语句,来检查博文状态并在这里改变其行为。这意味着需要查看很多位置来理解处于发布状态的博文的所有逻辑!这在增加更多状态时会变得更糟:每一个 `match` 语句都会需要另一个分支。
@ -340,7 +340,7 @@ impl State for Published {
另一个重复是 `Post``request_review``approve` 这两个类似的实现。他们都委托调用了 `state` 字段中 `Option` 值的同一方法,并在结果中为 `state` 字段设置了新值。如果 `Post` 中的很多方法都遵循这个模式,我们可能会考虑定义一个宏来消除重复(查看第十九章的 [“宏”][macros] 部分)。
完全按照面向对象语言的定义实现这个模式并没有尽可能利用 Rust 的优势。让我们看看一些代码中可以做出的修改,来将无效的状态和状态转移变为编译时错误。
完全按照面向对象语言的定义实现这个模式并没有尽可能利用 Rust 的优势。让我们看看一些代码中可以做出的修改,来将无效的状态和状态转移变为编译时错误。
#### 将状态和行为编码为类型