重要提示:为了轻松阅读本文,您需要能够阅读Rust中的源代码并理解为什么将所有内容包装Rc<RefCell<...>>
不好的原因。
介绍
Rust通常不被认为是一种面向对象的语言:没有实现继承。乍一看,也没有封装;最后,OOP专家熟悉的可变对象的依赖关系图看起来尽可能丑陋(只需查看所有这些Rc<RefCell<...>>
和Arc<Mutex<...>>
!)。
的确,实现继承已经被认为有害多年了,并且OOP专家说非常正确的事情,例如“一个好的对象就是一个不变的对象”。所以我想知道:“对象思维”和“ Rust”真正融合在一起的程度如何?
第一只豚鼠将是国家模式,本文的主题是国家模式的完全实施。
选择它是有原因的:The Rust Book中的一章专门讨论了相同的模式。该章的目的是说明只有坏男孩和女孩才能在Rust中编写面向对象的代码:在这里,您Option
需要将不必要的和琐碎的方法实现复制并粘贴到该特征的所有实现中。但是,如果您应用了一些技巧,整个样板将消失,可读性将提高。
工作规模
原始文章为博客文章的工作流程建模。让我们展示我们的想象力,并使原始描述适应严酷的俄罗斯现实:
- 关于Habré的任何文章都曾经是一个空的草稿,作者必须填充其内容。
- 文章准备好后,将其发送以进行审核。
- 主持人批准该文章后,该文章就会在Habré上发布。
- 在文章发表之前,用户不应看到其内容。
对该文章进行的任何非法操作均无效(例如,您不能从沙箱中发布未经批准的文章)。
下面的清单演示了与上面的描述相对应的代码。
// main.rs
use article::Article;
mod article;
fn main() {
let mut article = Article::empty();
article.add_text("Rust -");
assert_eq!(None, article.content());
article.send_to_moderators();
assert_eq!(None, article.content());
article.publish();
assert_eq!(Some("Rust -"), article.content());
}
Article
到目前为止看起来像这样:
// article/mod.rs
pub struct Article;
impl Article {
pub fn empty() -> Self {
Self
}
pub fn add_text(&self, _text: &str) {
// no-op
}
pub fn content(&self) -> Option<&str> {
None
}
pub fn send_to_moderators(&self) {
// no-op
}
pub fn publish(&self) {
// no-op
}
}
这将遍历除最后一个之外的所有断言。不错!
模式实施
让我们添加一个空的trait State
,一个stateDraft
和几个字段到Article
:
// article/state.rs
pub trait State {
// empty
}
// article/states.rs
use super::state::State;
pub struct Draft;
impl State for Draft {
// nothing
}
// article/mod.rs
use state::State;
use states::Draft;
mod state;
mod states;
pub struct Article {
state: Box<dyn State>,
content: String,
}
impl Article {
pub fn empty() -> Self {
Self {
state: Box::new(Draft),
content: String::new(),
}
}
// ...
}
与麻烦 出发 设计
State
, . , - :
trait State {
fn send_to_moderators(&mut self) -> &dyn State;
}
, , , — .
?
pub trait State {
fn send_to_moderators(&mut self) -> Box<dyn State>;
}
. . , ?
:
pub trait State {
fn send_to_moderators(self: Box<Self>) -> Box<dyn State>;
}
: ( self
). , Self: Sized
, .. . trait object, .. .
: , , , . , , ; , .
use crate::article::Article;
pub trait State {
fn send_to_moderators(&mut self) -> Transit {
Transit(None)
}
}
pub struct Transit(pub Option<Box<dyn State>>);
impl Transit {
pub fn to(state: impl State + 'static) -> Self {
Self(Some(Box::new(state)))
}
pub fn apply(self, article: &mut Article) -> Option<()> {
article.state = self.0?;
Some(())
}
}
, , Draft
:
// article/states.rs
use super::state::{State, Transit};
pub struct Draft;
impl State for Draft {
fn send_to_moderators(&mut self) -> Transit {
Transit::to(PendingReview)
}
}
pub struct PendingReview;
impl State for PendingReview {
// nothing
}
// article/mod.rs
impl Article {
// ...
pub fn send_to_moderators(&mut self) {
self.state.send_to_moderators().apply(self);
}
// ...
}
-
: Published
, State
, publish
PendingReview
. Article::publish
:)
. content
State
, Published
, , Article
:
// article/mod.rs
impl Article {
// ...
pub fn content(&self) -> Option<&str> {
self.state.content(self)
}
// ...
}
// article/state.rs
pub trait State {
// ...
fn content<'a>(&self, _article: &'a Article) -> Option<&'a str> {
None
}
}
// article/states.rs
impl State for Published {
fn content<'a>(&self, article: &'a Article) -> Option<&'a str> {
Some(&article.content)
}
}
, ? , !
impl Article {
// ...
pub fn add_text(&mut self, text: &str) {
self.content.push_str(text);
}
// ...
}
( ) , .
! !
, Article
, - , , . ? , ! .
, - Rust , , . - -.
, , Rust . , Observer: , Arc<Mutex<...>>
!
, .