58_实现面向对象的设计模式-状态模式

1. 状态模式

状态模式(state pattern)是一种面向对象的设计模式,一个值拥有的内部状态由数个状态对象(state object)表达而成,而值的行为则随着内部状态的改变而改变。

使用状态模式意味着:业务需求不断变化时,不需要修改持有状态的值的代码,或者使用这个值的代码。只需要更新状态对象内部的代码,以改变其规则。或者增加一些新的状态对象。

看一个案例:分别用状态模式和状态和行为为编码为类型两种方式实现发布博客流程。

2. 状态模式

main.rs

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());
}

lib.rs

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 类型检查系统会通过编译时错误来阻止用户使用无效的状态

main.rs

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());
}

lib.rs

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具有所有权等其它面向对象语言没有的特性!

相关推荐
Shi_haoliu1 小时前
openClaw源码部署-linux
前端·python·ai·openclaw
程序员小寒2 小时前
前端性能优化之白屏、卡顿指标和网络环境采集篇
前端·javascript·网络·性能优化
烛阴2 小时前
Claude CLI AskUserQuestion 工具详解:让 AI 开口问你
前端·claude
wal13145202 小时前
OpenClaw教程(九)—— 彻底告别!OpenClaw 卸载不残留指南
前端·网络·人工智能·chrome·安全·openclaw
mon_star°3 小时前
在TypeScript中,接口MenuItem定义中,为什么有的属性带问号?,有的不带呢?
前端
牛奶3 小时前
分享一个开源项目,让 AI 辅助开发真正高效起来
前端·人工智能·全栈
次顶级4 小时前
表单多文件上传和其他参数处理
前端·javascript·html
why技术4 小时前
我拿到了腾讯QClaw的内测码,然后沉默了。
前端·后端
谪星·阿凯5 小时前
XSS漏洞解析博客
前端·web安全·xss