Skip to content

面向对象

面向对象程序由对象组成。一个对象封装了数据和操作这些数据的过程。这些过程通常被称为方法操作

  • 带有方法的结构体和枚举,作用与对象相似,但官方刻意不称之为对象

Trait Object

(太几把难学了,跟异步有的一拼。句子都看得懂,代码就看不懂了。以后再回来写。)

根据后文的理解反推:

  • 程序有一个正经的结构体(主结构体),用于存放数据(包括对不正经结构体的引用)和实现方法
  • 程序有多个不正经的结构体(子结构体),结构体的名字仅用于标记主对象(主结构体的实例)的状态,本身不存储数据
  • 子结构体有实现方法,但 impl 内的方法 被 trait 限制,而且是多个子结构体实现同一个 trait
  • Trait对象(Trait Object):就是子结构体(不正经结构体)的实例,但通过被主结构体引用来使用内部方法
  • Trait对象作为主对象的状态标记是可以动态分发的,通过改变引用即可改变状态,同时就能改变同名方法的行为。实现了对象在不同状态下,同一函数(只是使用时名字和路径都一样,而且受限于同一个Trait,实际上不是同一个函数)的不同行为

面向对象的状态实现

教程的其他内容逼逼赖赖的,但一看代码就懂了,跟前面的内容完全反过来了,草。

基于 trait 对象的状态实现

rust
// 定义 Post 结构体,包含状态和内容
pub struct Post {
    state: Option<Box<dyn State>>,  // 使用 Option<Box<dyn State>> 存储状态对象
    content: String,                // 博文内容
}

impl Post {
    // 创建新的草稿博文
    pub fn new() -> Post {
        Post {
            state: Some(Box::new(Draft {})),  // 初始状态为 Draft
            content: String::new(),            // 初始内容为空
        }
    }

    // 向博文添加文本
    pub fn add_text(&mut self, text: &str) {
        self.content.push_str(text);  // 直接修改内容,不依赖状态
    }

    // 获取博文内容(根据当前状态返回不同值)
    pub fn content(&self) -> &str {
        // 调用当前状态的 content 方法
        self.state.as_ref().unwrap().content(self)
    }

    // 请求审核,改变状态
    pub fn request_review(&mut self) {
        // 取出当前状态(留下 None),然后转移所有权
        if let Some(s) = self.state.take() {
            // 调用状态的 request_review 方法获取新状态
            self.state = Some(s.request_review())
        }
    }

    // 批准发布,改变状态
    pub fn approve(&mut self) {
        if let Some(s) = self.state.take() {
            self.state = Some(s.approve())
        }
    }
}

// 状态 trait,定义所有状态共有的行为
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
    }
}
rust
use blog::Post;

fn main() {
    let mut post = Post::new();

    // 草稿阶段添加内容
    post.add_text("I ate a salad for lunch today");
    assert_eq!("", post.content());  // 草稿状态内容应为空

    // 请求审核
    post.request_review();
    assert_eq!("", post.content());  // 等待审核状态内容应为空

    // 批准发布
    post.approve();
    assert_eq!("I ate a salad for lunch today", post.content());  // 已发布状态显示内容
}

基于类型的状态实现

rust
// 已发布的博文
pub struct Post {
    content: String,  // 只有发布状态才能访问内容
}

// 草稿博文
pub struct DraftPost {
    content: String,
}

// 等待审核的博文
pub struct PendingReviewPost {
    content: String,
}

impl Post {
    // 创建新的草稿博文(直接返回 DraftPost)
    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,
        }
    }
}

impl PendingReviewPost {
    // 从等待审核转为已发布状态
    pub fn approve(self) -> Post {
        Post {
            content: self.content,
        }
    }
}
rust
use blog::Post;

fn main() {
    // 创建草稿
    let mut post = Post::new();
    
    // 添加内容(只能在草稿阶段)
    post.add_text("I ate a salad for lunch today");
    
    // 请求审核(消耗 DraftPost,返回 PendingReviewPost)
    let post = post.request_review();
    
    // 批准发布(消耗 PendingReviewPost,返回 Post)
    let post = post.approve();
    
    // 现在可以访问内容
    assert_eq!("I ate a salad for lunch today", post.content());
}