1. 状态模式
状态模式(state pattern)是一种面向对象的设计模式,一个值拥有的内部状态由数个状态对象(state object)表达而成,而值的行为则随着内部状态的改变而改变。
使用状态模式意味着:业务需求不断变化时,不需要修改持有状态的值的代码,或者使用这个值的代码。只需要更新状态对象内部的代码,以改变其规则。或者增加一些新的状态对象。
看一个案例:分别用状态模式和状态和行为为编码为类型两种方式实现发布博客流程。
2. 状态模式
rs
use blog::Post;
fn main() {
// 可变
let mut post = Post::new();
post.add_text("This is a post.");
asset_eq!("", post.content());
post.request_review();
asset_eq!("", post.content());
// 审批完成发布
post.approve();
asset_eq!("This is a post.", post.content());
}
rs
pub struct Post {
state: Option<Box<dyn State>>, // 草稿、等待审批、已发布三种状态
content: String,
}
impl Post {
pub fn new() -> Post {
Post {
state: Some(Box::new(Draft {})),
content: String::new(),
}
}
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
pub fn content(&self) -> &str {
self.state.as_ref().unwrap().content(&self)
}
pub fn request_review(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.request_review());
}
}
pub fn approve(&mut self) {
if let Some(s) = self.state.take() {
self.state = Some(s.approve());
}
}
}
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 {}
impl State for Draft {
fn request_review(self: Box<Self>) -> Box<dyn State> {
Box::new(PendingReview {})
}
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
}
// 审核
struct PendingReview {}
impl State for PendingReview {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
fn approve(self: Box<Self>) -> Box<dyn State> {
Box::new(Published {})
}
}
// 发布
struct Published {}
impl State for Published {
fn request_review(self: Box<Self>) -> Box<dyn State> {
self
}
fn approve(self: Box<Self>) -> Box<dyn State> {
self
}
fn content<'a>(&self, post: &'a Post) -> &'a str{
&post.content
}
}
可以看到状态模式,每个状态维护自身的流程。
缺点:
- 某些状态之间是相互耦合的
- 需要重复实现一些逻辑代码
3. 状态和行为编码为类型
结合rust的特点修改上面代码:
将状态编码为不同的类型:
- Rust 类型检查系统会通过编译时错误来阻止用户使用无效的状态
ini
use blog::Post;
fn main() {
let mut post = Post::ngw();
post.add_text("I ate a salad for lunch today");
let post = post.request_review();
let post = post.approve();
assert_eq!("I ate a salad for lunch today", post.content());
}
rs
pub struct Post {
content: String,
}
pub struct DraftPost {
content: String,
}
// 发布成功的
impl Post {
pub fn new() -> DraftPost {
DraftPost {
content: String::new(),
}
}
pub fn content(&self) -> &str {
&self.content
}
}
// 草稿
impl DraftPost {
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
pub fn request_review(self) -> PendingReviewPost {
PendingReviewPost {
content: self.content,
}
}
}
pub struct PendingReview {
content: String,
}
impl PendingReview {
pub fn approve(self) -> Post {
Post {
content: self.content,
}
}
}
只能在对应的状态做对应的事情,没有提供额外的方法。
4. 总结
Rust不仅能够实现面向对象的设计模式,还可以支持更多的模式。例如:将状态和行为编码为类型;面向对象的经典模式并不总是Rust编程实践中的最佳选择,因为Rust具有所有权等其它面向对象语言没有的特性!