织梦程序来搭建网站,顺义企业建站,海南学校网站建设,建设信用网站的作用状态模式#xff08;state pattern#xff09;是一种面向对象的设计#xff0c;它的关键点在于#xff1a;一个值拥有的内部状态由数个状态对象#xff08;state object#xff09;表的而成#xff0c;而值的行为则随着内部状态的改变而改变。
下面的示例用来实现发布博…状态模式state pattern是一种面向对象的设计它的关键点在于一个值拥有的内部状态由数个状态对象state object表的而成而值的行为则随着内部状态的改变而改变。
下面的示例用来实现发布博客的工作流程
在新建博客时生成一个空白的草稿文档状态是Draft。在草稿编辑完毕后请求对这个文章的状态进行审批request_review文档此时状态切换成了PendingReview。文章通过审批后对外正式发布状态为Published。仅返回并打印成功发布后的文章其他状态的文章都应该是对外不可见的
State trait定义了文章状态共享的行为状态Draft、PendReview、Published都会实现State trait。
State trait中request_review声明中第一个参数的类型是self: BoxSelf而不是self、self或mut self。这个语法意味着该方法只能被包裹着当前实例的Box调用它会在调用过程中获取BoxSelf的所有权并使旧的状态失效从而将Post状态转换为一个新的状态。
// lib.rs
trait State {fn request_review(self: BoxSelf) - Boxdyn State;fn approve(self: BoxSelf) - Boxdyn State;fn contenta(self, post: a Post) - a str;
}pub struct Post {state: OptionBoxdyn 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())}}
}struct Draft {}impl State for Draft {fn request_review(self: BoxSelf) - Boxdyn State {Box::new(PendingReview {})}fn approve(self: BoxSelf) - Boxdyn State {self}fn contenta(self, post: a Post) - a str {}
}struct PendingReview {}impl State for PendingReview {fn request_review(self: BoxSelf) - Boxdyn State {self}fn approve(self: BoxSelf) - Boxdyn State {Box::new(Published {})}fn contenta(self, post: a Post) - a str {}
}struct Published {}impl State for Published {fn request_review(self: BoxSelf) - Boxdyn State {self}fn approve(self: BoxSelf) - Boxdyn State {self}fn contenta(self, post: a Post) - a str {post.content}
}request_review
为了消耗旧的状态request_review方法需要获取状态值的所有权。这也正是Post的state字段引入Option的原因RUST不允许结构体中出现未被填充的值。我们可以通过OptionT的take方法来取出state字段的Some值并在原来的位置留下一个None。
我们需要临时把state设置为None来取得state值的所有权而不能直接使用self.state self.state.request_review()这种代码这可以确保Post无法在我们完成状态转换后再次使用旧的state值。 take方法的作用Takes the value out of the option, leaving a [None] in its place. content
content方法体中调用了Option的as_ref方法因为我们需要的只是Option中值的引用而不是它的所有权。由于state的类型是OptionBoxdyn State所以我们在调用as_ref时得到OptionBoxdyn State。如果这段代码中没有调用as_ref就会导致编译错误因为我们不能将state从函数参数的借用self中移出。
我们需要在这个方法上添加相关的声明周期标注该方法接受post的引用作为参数并返回post中的content作为结果因此该方法中返回值的声明周期应该与post参数的声明周期相关。 as_ref方法声明Converts from OptionT to OptionT但例子中属于OptionT到OptionT的转换 代码冗余
示例的代码存在一个缺点Draft、PendReview、Published重复实现了一些代码逻辑。你也许会试着提供默认实现让State trait的request_review和approve方法默认返回self。但这样的代码违背了对象安全规则因为trait无法确定self的具体类型究竟是什么。如果我们希望将State作为trait对象来使用那么它的方法就必须全部是对象安全的。
use
代码中使用了main.rs和lib.rs两个文件在lib.rs也没有做任何mod的声明。在main.rs中通过使用blog::Post路径进行导而不是crate::Post。
根路径blog和我们配置文件Cargo.toml中package.name的声明有关系根路径直接使用了包的名字。
// main.rs
use blog::Post;fn main() {let mut post Post::new();post.add_text(l go out to play);assert_eq!(, post.content());post.request_review();assert_eq!(, post.content());post.approve();assert_eq!(l go out to play, post.content());
}