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

相关推荐
程序员黑豆1 小时前
鸿蒙应用开发:V1与V2版本数据持久化实战教程
前端·harmonyos
小月土星5 小时前
React Router 前端路由深度解析:从传统后端路由到现代 SPA 的完整演进
前端
程序员黑豆5 小时前
鸿蒙开发 Navigation 路由教程:从入门到实战
前端·harmonyos
用户938515635078 小时前
从零理解 React Router v6:每一个 API 都是怎么工作的
前端·javascript·全栈
用户0595401744610 小时前
Redis缓存回滚踩坑实录:一个配置失误,让我在凌晨3点丢了3000条数据
前端·css
kyriewen10 小时前
别再这样写条件渲染了——你的React组件里藏着这5种定时炸弹
前端·javascript·react.js
IT_陈寒11 小时前
Redis缓存击穿把我坑惨了,原来这样设过期时间才靠谱
前端·人工智能·后端
用户9385156350711 小时前
从"坐电梯"到"前端路由"——深入理解 Hash 路由原理
前端·typescript·全栈
梦想CAD控件11 小时前
网页端CAD的图形选择、编辑与夹点操作教程
前端·javascript·node.js
妙码生花11 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(五十二):管理员权限检查中间件,AI 随意放行预闯大祸
前端·后端·go