@@ -1,30 +1,34 @@
# 实现面向对象的设计模式
* 状态模式, the state pattern * 属于一种面向对象设计模式。这种模式的核心,便是咱们要定义某个值在其内部可能 有的一套各种 状态。这些状态是 由一套 * 状态对象, state objects * 所 表示的,同时该 值的行为, 会根据其状态而改变。接下来咱们会完成有着 保存其状态字段,该字段将有着 “草稿, draft”、“审阅, review ” 或“已发布, published ” 三种 状态集合的状态对象,的一个博客帖子结构体示例 。
* 状态模式, the state pattern * , 属于一种面向对象的 设计模式。这种模式的关键在于,我们定义某个值可能在内部具 有的一套状态。这些状态由一组 * 状态对象, state objects * 表示,而 值的行为会根据其状态而变化。我们即将进行一个博客帖子结构体的示例,其有着一个 保存其状态的 字段,该字段将为 “草稿”、“审阅 ” 或 “已发布” 三个 状态集中的一个状态对象 。
状态对象共用着 功能:当然,在 Rust 中咱们会 使用结构体与特质,而非对象与继承。每个状态对象负责其 自己的行为,以及在其应变 换为另一状态时自身的治理 。保存着 状态对象的值,对这些 状态的不同行为,或这些状态之间何时变换就毫不知情 。
状态对象共用功能:当然,在 Rust 中,我们 使用结构体与特质,而非对象与继承。每个状态对象都 负责自己的行为,并管理何时应转 换为另一种 状态。保存状态对象的值,对状态的不同行为,或何时进行状态转换一无所知 。
运 用状态模式的优势在于,当程序的业务需求改变时,咱们将不需要修改该值 保存状态的那些 代码,也不需要修改 用到该值的那些 代码。咱 们只需更新某个状态对象内部的那些代码,来改变 其规则,或是 添加别的一些 状态对象。
使 用状态模式的优势在于,当程序的业务需求发生变化时,我们无需修改 保存状态的值的 代码,或 用到该值的代码。我 们只需更新某个状态对象内部的代码即可更改 其规则,或者 添加更多 状态对象。
首先,咱 们将要 以更传统的面向对象方式, 实现这种 状态模式,随后咱 们将使用对于 Rust 中, 更自然一些的方法。下面就来深入到 使用状态模式,逐步实现一个 博客帖子工作流。
首先,我 们将以更传统的面向对象方式实现状态模式,然后,我 们将使用一种在 Rust 中更自然的方式。我们来深入研究如何 使用状态模式,逐步实现博客帖子的 工作流。
最终功能看起来将 像下面这样:
最终的 功能将 看起来像下面这样:
1. 博客帖子作为空白的草稿开始;
2. 草稿完成后,对帖子的审阅是必需的;
3. 帖子被批准后,其得以发布;
4. 只有已发布的博客帖子才会返回用以打印的内容,从而未获批准的帖子不会意外发布。
对帖子尝试的任何其他修改均应无效。例如,当我们在请求审核之前尝试批准博客帖子草稿时,该帖子应保持为未发布的草稿。
1. 博客帖子以一个空的草稿开始;
2. 在草稿写好后,该帖子就要求审阅一下;
3. 在帖子被批准后,其就会被发布;
4. 只有发布了的帖子,才会返回要打印的内容,因此那些未获批准的帖子就不会被无故发布。
## 尝试传统的面向对象风格
为了解决同一个问题,代码的结构方式有无数种,每种都有不同的权衡取舍。这一节的实现更多的是传统的面向对象风格,虽然在 Rust 中可以这样编写,但并未利用 Rust 的某些优势。稍后,我们将演示一种不同的解决方案,他虽然仍采用面向对象的设计模式,但其结构方式对于有面向对象编程经验的开发者而言,可能显得不太熟悉。我们将比较这两种方案,以体验到与其他语言的代码相比,以不同方式设计 Rust 代码的权衡取舍。
所有别的在帖子上的尝试修改,都应无效。比如,在完成审阅之前,若咱们尝试批准博客帖子草稿,那么该帖子应保持为一个未发布的草稿 。
下面清单 17-11 给出了代码形式的这个工作流:此为咱们将在一个名为 `simple_blog` 的库代码箱中,实现的这个 API 的一个示例用法。由于咱们尚未实现该 `simple_blog` 代码箱,因此这段代码尚不会编译。
下面清单 18-11 以代码形式展示了这一工作流程:这是我们将在名为 `blog` 的库代码箱中实现的 API 的一个使用示例。这还不会编译,因为我们尚未实现 `blog` 代码箱 。
<a name="listing_18-11"></a>
文件名:`src/main.rs`
``` rust
use simple_ blog ::Post ;
use blog ::Post ;
fn main ( ) {
let mut post = Post ::new ( ) ;
@@ -40,24 +44,22 @@ fn main() {
}
```
* 清单 17 -11:验证咱们打算这个 `simple_ blog` 代码箱要有的必要功能的代码 *
** 清单 18 -11** :演示我们希望 ` blog` 代码箱具备的预期行为的代码
咱们打算运行 用户使用 `Post::new` 创建出一个 新的帖子草稿。咱 们打算允许将一些文本 添加到博客帖子。当咱们 在审批之前,尝试 立即获取到 帖子的内容时,由于该帖子仍为一个草稿,因此咱们就不应得到任何文本 。出于验证 目的,咱们已在该 代码中添加了 `assert_eq!` 。而为此目的的良好 单元测试,就应 断言帖子草稿会从那个 `content` 方法返回空字符串,而咱们并未打算为此 示例编写一些 测试。
我们希望允许 用户使用 `Post::new` , 创建新的博客 帖子草稿。我 们打算允许添加文本 到博客帖子。当我们尝试 在审批之前立即获取帖子的内容时,我们不应得到任何文本,因为该帖子仍然是草稿 。出于演示 目的,我们在 代码中添加了 `assert_eq!` 。针对这点的理想 单元测试是 ,断言帖子草稿会从 `content` 方法返回空字符串,但我们不会为这个 示例编写测试。
接下来,咱们打算开启该 帖子的审阅请求,同时咱们系统 在等待审阅期间,`content` 返回一个 空字符串。在该帖子得到审批时 ,他就 应得以发布了,表示在 `content` 被调用时,该帖子的文本 将被返回。
接下来,我们希望启用对 帖子的一次 审阅请求,并且我们希望 在等待审阅期间,`content` 返回空字符串。当帖子获得批准后 ,他应得以发布,这意味着当 `content` 被调用时,该帖子的正 文将被返回。
请注意咱们与 `simple_blog` 代码箱交互的唯一类型,便 是 `Post` 这个 类型。此 类型将用到 状态模式,并将 保存着将为 表示帖子可能状态 -- 草稿、等待审阅或已发布,的三个状态对象之一的一个值 。从一种状态改变为 另一状态,将在该 `Post` 类型里 得以内部管理。这些状态,会因应着 库用户在 `Post` 实例上 的方法调用而改变 ,但库 用户们却 不必直接管理这些状态改变。同样,用户们是无法在这些状态上犯下错误的 ,比如在帖子未 审阅前发布帖子。
请注意,我们与该 代码箱中 交互的唯一类型是 `Post` 类型。这个 类型将使 用状态模式,并保存一个值,该值将是三个状态对象之一, 表示帖子可能出于的不同 状态 -- 草稿、等待审阅或已发布。从一种状态到 另一状态的更改 ,将在 `Post` 类型内部 得以内部地 管理。状态的变化,是响应于 库用户对 `Post` 实例调用 的方法而发生的 ,但用户不必直接管理状态变更。此外,用户也不会在状态方面犯错 ,比如在审阅前发布帖子。
## 定义出 `Post` 并创建出一个草稿状态的 新实例
### 定义 `Post` 并创建新实例
**Defining `Post` and Creating a New Instance in the Draft State **
我们来开始库的实现!我们知道我们需要一个公开的 `Post` 结构体保存一些内容,因此我们将从该结构体的定义,及用于创建 `Post` 实例的关联公开 `new` 函数开始,如下清单 18-12 中所示。我们还将构造一个私有的 `State` 特质,将定义某个 `Post` 的所有状态对象必须具备的行为。
然后,`Post` 类型将在名为 `state` 的私有字段的 `Option<T>` 值内,保存 `Box<dyn State>` 的特质对象,以保存状态对象。稍后咱们就会明白为何 `Option<T>` 是必要的。
下面就来开始这个库的实现!咱们清楚咱们需要保存着一些内容的一个公开的 `Post` 结构体,因此咱们将以这个结构体的定义,及创建出 `Post` 实例的一个关联的公开 `new` 函数开始,如下清单 17-12 中所示。咱们还将构造出将定义 `Post` 的全部状态对象,所必须有的行为的一个私有 `State` 特质。
随后 `Post` 类型将在名为 `state` 的私有字段的 `Option<T>` 值内部,保存 `Box<dyn State>` 类型的一个特质对象,来保存状态对象。过一会儿,咱们就会看到为何那个 `Option<T>` 是必要的。
<a name="listing_18-12"></a>
文件名:`src/lib.rs`
``` rust
@@ -82,75 +84,65 @@ struct Draft {}
impl State for Draft { }
```
* 清单 17 -12: `Post` 结构体的定义,以及 创建新 `Post` 实例的 `new` 函数、`State` 特质,以及 `Draft` 结构体*
** 清单 18 -12** : `Post` 结构体的定义,和 创建新 `Post` 实例的 `new` 函数、 `State` 特质,以及 `Draft` 结构体
其中的 `State` 特质会 定义出由 不同帖子状态共用的行为。这些 状态对象分别是 `Draft` 、`PendingReview` 及 `Published` , 同时 他们都将实现 `State` 特质。至于现在 ,这个 特质并无 任何方法,而由于 `Draft` 状态是咱们想要帖子开始的状态,因此咱们将以仅定义出这个状态开始 。
其中 `State` 特质定义了 不同帖子状态共用的行为。状态对象分别是 `Draft` 、`PendingReview` 和 `Published` ,他们都将实现 `State` 特质。目前 ,这一 特质没有 任何方法,且我们以仅定义 `Draft` 状态开始,因为这是我们的帖子开始的状态 。
在 创建出 新的 `Post` 实例时,咱们将 其 `state` 自动设置为了保存着 一个 `Box` 值的 `Some` 值。这个 `Box` 会只想 `Draft` 结构体的一个 新实例。这会 确保不能在咱们何时 创建出 一个 `Post` 的新实例,其都将作为一篇草稿 开始。由于 `Post` 的 `state` 字段是私有的,因此就 没有办法创建出其他任何 状态的一个 `Post` !在 `Post::new` 函数中,咱们把 `content` 字段设置为了 一个新的、空 `String` 。
当我们 创建新的 `Post` 实例时,我们设置 其 `state` 字段为包含 一个 `Box` 值的 `Some` 值。这个 `Box` 值指向一个 `Draft` 结构体的新实例。这确保了每当我们 创建一个 `Post` 的新实例时,他都将以草稿形式 开始。由于 `Post` 的 `state` 字段是私有的,因此没有办法创建处于任何其他 状态的 `Post` !在 `Post::new` 函数中,我们设置 `content` 字段为 一个新的、空 `String` 。
## 存储帖子内容文本
### 存储帖子内容的 文本
**Storing the Text of the Post Content **
在 17-11 中,咱们曾看到咱们希望能调用一个名为 `add_text` 的方法,并传递给他随后被作为博客帖子内容而添加的一个 `&str` 。咱们将这实现为一个方法,而不是把 `content` 作为 `pub` 暴露出来,如此稍后咱们就可以实现一个将控制 `content` 字段数据如何被读取的方法。这个 `add_text` 方法是相当直截了当的,那么接下就来在清单 17-13 中,添加这个实现到 `impl Post` 代码块:
我们在清单 18-11 中看到,我们希望能够调用一个名为 `add_text` 的方法,并传递给他一个 `&str` ,然后添加为博客帖子的文本内容。我们会作为方法实现这点,而不是暴露 `content` 字段为 `pub` ,以便稍后我们可以实现一个方法,其将控制 `content` 字段的数据被读取的方式。`add_text` 方法相当简单,因此我们来添加下面清单 18-13 中的实现到 `impl Post` 代码块。
<a name="listing_18-13"></a>
文件名:`src/lib.rs`
``` rust
impl Post {
// -- 跳过代码 --
pub fn add_text ( & mut self , text : & str ) {
self . content . push_str ( text ) ;
}
}
```
* 清单 17 -13:实现将文本添加到帖子 `content` 字段的 `add_text` 方法 *
** 清单 18 -13** :实现 `add_text` 方法,以添加文本到帖子的 `content` 字段
由于咱们是在修改咱们正于其上调用 `add_text` 的 `Post` 实例,因此这个 `add_text` 方法便取了到 `self` 的可变引用。咱们随后调用了 `content` 字段中 `String` 类型上的 `push_str` , 并传递 `text` 参数来 添加到那个 已保存的 `content` 。此 行为不依赖帖子所处的状态,因此其并非 状态模式的一部分。这个 `add_text` 方法完全不与 `state` 字段交互,但其为咱们打算 支持行为的一部分。
`add_text` 方法取到 `self` 的可变引用,因为我们正在修改我们正对其调用 `add_text` 的 `Post` 实例。然后,我们对 `content` 字段中的 `String` 值调用 `push_str` 并传递 `text` 参数,以 添加到已保存的 `content` 。这一 行为不依赖于 帖子所处的状态,因此他不是 状态模式的一部分。`add_text` 方法完全不与 `state` 字段交互,但他是我们希望 支持的 行为的一部分。
## 确保帖子 草稿的内容为空
### 确保草稿帖子 的内容为空
**Ensuring the Content of a Draft Post Is Empty **
即使咱们已调用了 `add_text` 并把一些内容添加到了咱们的帖子,但由于该帖子仍处于草稿状态,故咱们仍想要那个 `content` 方法返回空字符串切片, an empty string slice, 正如清单 17-11 中第 7 行所给出的那样。那么现在,就来用将满足此要求的最简单物件,实现这个 `content` 方法:即总是返回一个空字符串切片。稍后一旦咱们实现修改帖子状态的能力,从而帖子可被发布,咱们就会修改这个方法。到目前为止,贴子就只能处于草稿状态,因此帖子内容应始终为空。下面清单 17-14 给出了这种占位的实现:
即使我们调用了 `add_text` 并添加了一些内容到帖子,我们仍然希望 `content` 方法返回一个空的字符串切片,因为帖子仍处于草稿状态,正如清单 18-11 中的第一个 `assert_eq!` 所示。目前,我们来以将满足这一要求的最简单方式实现 `content` 方法:始终返回一个空字符串切片。一旦稍后实现修改帖子的状态以便其可被发布的能力后,我们将修改这个方法。到目前为止,贴子只能处于草稿状态,因此帖子内容应始终为空。下面清单 18-14 展示了这一占位符实现, a placehoder implementation。
<a name="listing_18-14"></a>
文件名:`src/lib.rs`
``` rust
impl Post {
// -- 跳过代码 --
pub fn content ( & self ) -> & str {
" "
}
}
```
* 清单 17 -14:添加始终返回空字符串切片的 `Post` 上 `content` 方法的一个 占位实现,a placeholder implementation *
** 清单 18 -14** :为 `Post` 上 `content` 方法添加 占位符 实现,其始终返回一个空字符串切片
有了 添加的这个 `content` 方法,清单 17 -11 中到第 7 行为止的那些代码就都将如预期那样工作了 。
通过这个 添加的 `content` 方法,清单 18 -11 中直 到第一个 `assert_eq!` 的所有代码都会按预期运行 。
## 请求帖子 审阅改变其 状态
**Requesting a Review of the Post Changes Its State **
接下来,咱们就需要添加请求帖子审阅的功能了,帖子审阅应将其状态从 `Draft` 改变为 `PendingReview` 。下面清单 17-15 给出了这样的代码:
### 请求审阅, 改变帖子的 状态
接下来,我们需要添加请求对帖子审阅的功能,这应该将其状态从 `Draft` 更改为 `PendingReview` 。下面清单 18-15 展示了这一代码。
<a name="listing_18-15"></a>
文件名:`src/lib.rs`
``` rust
impl Post {
// -- 跳过代码 --
pub fn request_review ( & mut self ) {
if let Some ( s ) = self . state . take ( ) {
self . state = Some ( s . request_review ( ) )
@@ -179,36 +171,33 @@ impl State for PendingReview {
}
```
* 清单 17 -15: 实现 `Post` 与 `State` 特质上的 `request_review` 方法*
** 清单 18 -15** :实现 `Post` 与 `State` 特质上的 `request_review` 方法
咱 们给了 `Post` 名为 `request_review` 的一个 公开方法,其将取到 `self` 的一个 可变引用。随后咱们调用了 `Post` 当前状态上的 内部 `request_review` 方法,而这第二个 `request_review` 方法就 会消费当前状态并返回一个新的 状态。
我 们给予 `Post` 一个 名为 `request_review` 的公开方法,其将取一个 到 `self` 的可变引用。然后,我们对 `Post` 的 当前状态调用 内部的 `request_review` 方法,而这第二个 `request_review` 方法会消费当前状态并返回一个新状态。
咱们把那个 `request_review` 方法添加到了 `State` 特质;所有实现了 这个特质的类型,现在都将需要实现这个 `request_review` 方法。请注意这里用的不再是 `self` 、`&self` 或 `&mut self` 作为该 方法的首 个参数,这里用的是 `self: Box<Self>` 。这样的语法表示,在有在某个保存了该 类型的 `Box` 上 调用时,这个方法才是 有效的 。这种语法会取得 `Box<Self>` 的所有权,令到原有 状态失效,进而 `Post` 的状态值就 可以转换到一种新的 状态。
我们添加 `request_review` 方法到 `State` 特质;所有实现这个特质的类型,现在都将需要实现 `request_review` 方法。请注意,我们没有使用 `self` 、`&self` 或 `&mut self` 作为这个 方法的第一 个参数,而是使用 `self: Box<Self>` 。这种语法意味着,这个方法仅在对包含这种 类型的 `Box` 调用时才 有效。这种语法会取得 `Box<Self>` 的所有权,从而使旧 状态失效,以便 `Post` 的状态值可以转换为新 状态。
为了消费原有 状态,这个 `request_review` 方法就 需要取得该 状态值的所有权。这便 是 `Post` 的那个 `state` 字段中的 `Option` 进入之处:咱 们调用了 `take` 方法(属于标准库的 `Option` 类型) ,来从 `state` 字段取出那个 `Some` 的 值,并由于 Rust 不允许咱 们在结构体中有无效或空字段, Rust doesn't let us have unpopulated fields in structs, 而在 `state` 字段中留下一个 `None` 。这样就允许咱们把其中的 `state` 值,迁移出 `Post` 而非借用他。随后咱们将把 帖子的 `state` 值,设置为此 操作的结果。
为了消费旧的 状态,`request_review` 方法需要取得状态值的所有权。这就 是 `Post` 的 `state` 字段中 `Option` 发挥作用的地方:我 们调用 `take` 方法,来从 `state` 字段取出 `Some` 值,并在其位置留下一个 `None` ,因为 Rust 不允许我 们在结构体中有着未填充的(无效或空字段) 字段。这让我们可以从 `Post` 中迁出 `state` 值,而不是借用他。然后,我们将设置 帖子的 `state` 值为这一 操作的结果。
为了获取到 `state` 值的所有权,咱们就 需要暂时将 `state` 设置 为 `None` ,而非直接使用像是 `self.state = self.state.request_review();` 这样的代码设置他。这样做 确保了在咱们已 将 `Post` 转换为新状态后,其 无法使用原先 的 `state` 值。
为了获的 `state` 值的所有权,我们 需要暂时设置 `state` 为 `None` ,而不是以 `self.state = self.state.request_review();` 这样的代码直接 设置他。这确保了在我们 将 `Post` 转换为新的 状态后,`Post` 无法再 使用旧 的 `state` 值。
`Draft` 上的 `request_review` 方法返回的是 个新的、新加入的 `PendingReview` 装箱过的 实例,其 表示了 帖子等待审阅时的状态。那个 `PendingReview` 结构体同样 实现了 `request_review` 方法,但并未进 行任何转换。而是,由于在咱们于某个 已处于 `PendingReview` 状态的帖子上, 请求审阅时,帖子 应保持处于 `PendingReview` 状态,因此 `PendingReview` 的 `request_review` 方法调用返回的是他自己 。
`Draft` 上的 `request_review` 方法返回一 个新的、装箱后的 新 `PendingReview` 结构体 实例,表示帖子等待审阅时的状态。`PendingReview` 结构体也 实现了 `request_review` 方法,但不执 行任何转换。相反,他会返回自身,因为当我们对 已处于 `PendingReview` 状态的帖子请求审阅时,他 应保持处于 `PendingReview` 状态。
现在咱们就 可以开始看到状态模式的优势了:不论 `Post` 的 `state` 值为何,其上的 `request_review` 方法都是 一样的 。每种状态都负责着其 自己的那些 规则。
现在我们 可以开始看到状态模式的优势了:不论 `Post` 的 `state` 值为何,其上的 `request_review` 方法都一样。每种状态都负责自己的规则。
咱 们将保留 `Post` 上的 `content` 方法如其现在这样,即 返回一个空字符串切片。现在咱们就 可以让某个 `Post` , 处于 `PendingReview` 状态抑或 `Draft` 状态了,不过咱 们想要 `PendingReview` 状态中 的同样行为。现在清单 17 -11 到第 10 行便 工作了!
我 们将保持 `Post` 上的 `content` 方法不变,使其 返回一个空字符串切片。我们现在既 可以让 `Post` 处于 `PendingReview` 状态,也可以处于 `Draft` 状态,但我 们想要 `PendingReview` 状态下 的同样行为。现在清单 18 -11 再第二个 `assert_eq!` 前都可以正常 工作了!
## 添加 `approve` 来 修改 `content` 的行为
## 添加 `approve` 以 修改 `content` 的行为
**Adding `approve` to Change the Behavior of `content` **
`approve` 方法将与 `request_review` 方法类似:他将把 `state` 设置为在帖子状态为 “批准” 时,当前状态所应表明的值,如下清单 17-16 中所示:
`approve` 方法将类似于 `request_review` 方法:他将设置 `state` 为当前状态规定的,再状态为 “批准” 时应具有的值,如下清单 18-16 中所示。
<a name="listing_18-16"></a>
文件名:`src/lib.rs`
``` rust
impl Post {
// -- 跳过代码 --
pub fn approve ( & mut self ) {
if let Some ( s ) = self . state . take ( ) {
self . state = Some ( s . approve ( ) )
@@ -254,29 +243,30 @@ impl State for Published {
}
```
* 清单 17 -16: 实现 `Post` 与 `State` 特质上的 `approve` 方法*
** 清单 18 -16** :实现 `Post` 与 `State` 特质上的 `approve` 方法
咱们把这个 `approve` 方法,添加到了 `State` 特质,并添加了 一个实现了 `State` 的新结构体,即 `Published` 状态。
我们添加 `approve` 方法到 `State` 特质,并添加一个实现 `State` 的新结构体,即 `Published` 状态。
与 `PendingReview` 上的 `request_review` 工作方式类似,在咱们调用 `Draft` 上的 `approve` 方法时,由于 `approve` 将返回 `self` ,因此他将没有效果。当咱们在 `PendingReview` 上 调用 `approve` 时,他返回的是 一个新的、装箱过 后的 `Published` 结构体实例。这个 `Published` 结构体实现了 `State` 特质,而由于帖子在 `request_review` 及 `approve` 两个方法下 ,都应保持处于 `Published` 状态,因此对于这两个方法,他都会返回他本身 。
与 `PendingReview` 上 `request_review` 的 工作方式类似,当我们对 `Draft` 调用 `approve` 方法时,他将不会产生效果,因为 `approve` 将返回 `self` 。在我们对 `PendingReview` 调用 `approve` 时,他返回一个新的、装箱后的 `Published` 结构体实例。`Published` 结构体实现了 `State` 特质,而对于 `request_review` 及 `approve` 这 两个方法,他都会返回自身,因为在这些情形下,帖子 都应保持处于 `Published` 状态。
现在咱们就 需要更新 `Post` 上的那个 `content` 方法了。咱 们希望从 `content` 返回的值, 取决于 `Post` 的当前状态,因此咱们就 让 `Post` , 委托给定义在其 `state` 上的一个 `content` 方法,如下清单 17 -17 中所示:
现在我们 需要更新 `Post` 上的 `content` 方法。我 们希望 `content` 返回的值取决于 `Post` 的当前状态,因此我们即将 让 `Post` 委托给定义在其 `state` 上的 `content` 方法,如下清单 18 -17 中所示。
<a name="listing_18-17"></a>
文件名:`src/lib.rs`
``` rust
impl Post {
// -- 跳过代码 --
pub fn content ( & self ) -> & str {
self . state . as_ref ( ) . unwrap ( ) . content ( self )
}
// -- 跳过代码 --
}
```
* 清单 17 -17:将 `Post` 上的 `content` 方法,更新 为委托给 `State` 上的 `content` 方法*
** 清单 18 -17** :更新 `Post` 上的 `content` 方法为, 委托给 `State` 上的 `content` 方法
> **译注**:这里涉及到一个 “委托”、“委派” 的概念, updating the `content` on `Post` to delegate to a `content` medthod on `State`
由于咱们的目标是要把所有规则,都保持于实现了 `State` 的那些结构体中,因此咱们就要调用 `state` 字段中值上的 `content` 方法,并将帖子实例(那就是 `self` )作为参数加以传递。随后咱们要返回从 `state` 值上的 `content` 方法调用所返回的值。