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

相关推荐
浮华似水12 分钟前
简洁之道 - React Hook Form
前端
正小安2 小时前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch4 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光4 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   4 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   4 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web4 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常4 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇5 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr5 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui