用 Rust 实现面向对象中的状态模式
使用 Rust 实现面向对象中的状态模式, 下面的代码模拟了一篇 Blog 的发布 (Post) 过程.
文章中涉及了一些 Rust 特性, 如果你是第一次接触 Rust, 可以先看这里.
准备工作
安装 Rust 环境. 看这里
新建工程, 准备 main.rs
和 lib.rs
文件.
$ cargo new blog
$ cd blog/src
$ touch lib.rc
$ ls
lib.rs main.rs
cargo 是 Rust 的构建工具和包管理器. cargo 的入门文章看这里.
测试程序
main.rs
use blog::Post;
#[allow(unused_variables, unused_mut, unused_assignments)]
fn main() {
// 新建一篇文章
let mut post = Post::new();
// 添加一些文字
post.add_text("I ate a salad for lunch today");
assert_eq!("", post.content()); // 文章还未发表, 看不到内容
// 请求审核
post.request_review();
assert_eq!("", post.content()); // 文章还未发表, 看不到内容
// 审核过程省略...(这里只是模拟, 真实的审核或许在其他线程进行)
// 审核通过
post.approve();
assert_eq!("I ate a salad for lunch today", post.content()); // 审核通过了, 文章可以发表, 可以看到文章的内容
}
Post 库
lib.rs
/* 发布一篇文章经历的几个状态
Draft: 草稿状态. 调用 request_review() 进入下一个状态
PendingReview: 待审核状态. 调用 approve() 进入下一个状态
Published: 已发布(审核ok)状态.
*/
// 定义 Post 结构
#[allow(unused_variables, unused_mut, unused_assignments)]
pub struct Post {
state: Option<Box<dyn State>>,
content: String,
}
// Post 类型的方法
#[allow(unused_variables, unused_mut, unused_assignments)]
impl Post {
// 创建一篇空白文章
pub fn new() -> Self {
Post {
state: Some(Box::new(Draft{})), // 文章的默认状态是草稿
content: String::new(),
}
}
// 添加内容
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
// 提交审核
pub fn request_review(&mut self) {
if let Some(local_state) = self.state.take() {
self.state = Some(local_state.request_review());
}
}
// 审核通过
pub fn approve(&mut self) {
if let Some(local_state) = self.state.take() {
self.state = Some(local_state.approve());
}
}
// 获取文章内容
pub fn content(&self) -> &str {
self.state.as_ref().unwrap().content(self)
}
}
// 文章的三个状态应该具有的共同方法. 也就是说每个具体状态内部都要实现这个三个方法.
trait State {
fn request_review(self: Box<Self>) -> Box<dyn State>;
fn approve(self: Box<Self>) -> Box<dyn State>;
fn content<'a>(&self, post: &'a Post) -> &'a str;
}
// 三种具体状态
struct Draft {} // "草稿" 状态. 等待提交审核
struct PendingReview {} // "待审核" 状态. 等待审核
struct Published {} // "已发布" 状态
// "草稿" 状态
#[allow(unused_variables, unused_mut, unused_assignments)]
impl State for Draft {
// 提交审查后, 状态转为等待审核
fn request_review(self: Box<Self>) -> Box<dyn State> {
Box::new(PendingReview{})
}
// 目前是草稿状态, 不能直接同意, 所以 Blog 的状态不能改变, 还是草稿
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
// 草稿状态的 Blog 不能发表, 所以返回空串
fn content<'a>(&self, post: &'a Post) -> &'a str {
""
}
}
// "待审核" 状态
#[allow(unused_variables, unused_mut, unused_assignments)]
impl State for PendingReview {
// blog 等待审核, 再次提交审核并不能改变 blog 的状态, 所以仍然是"待审核"状态
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
// 同意, 则 blog 可以发布了, 状态改为 "已发布"
fn approve(self: Box<Self>) -> Box<dyn State> {
Box::new(Published{})
}
// blog 等待审核, 还没定稿, 不能发表, 返回空串
fn content<'a>(&self, post: &'a Post) -> &'a str {
""
}
}
// "已发布" 状态
#[allow(unused_variables, unused_mut, unused_assignments)]
impl State for Published {
// blog 已发布, 没有请求审核的必要, 保持现有状态
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
// blog 已发布, 没有审核的必要, 保持现有状态
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
// blog 已发布, 可以看到内容.
fn content<'a>(&self, post: &'a Post) -> &'a str {
&post.content
}
}
原文看这里.