Rust语言之GoF设计模式:状态模式


状态模式与有限状态机 (FSM) 概念相关,但是,每个状态都由实现公共状态特征的单独类型表示,而不是实现大量条件语句。
状态之间的转换取决于每种状态类型的特定 trait 实现。
Rust 中的状态模式在Rust Book中有详细描述: https://doc.rust-lang.org/book/ch17-03-oo-design-patterns.html

状态模式经常与命令模式 混合使用,因为一个命令执行以后,必然带来状态的改变,命令促使状态变化,状态变化也可以使用事件活动表达,一个命令下达被执行以后,发出领域事件 ,这是事件溯源 的原理,事件溯源是一种 备忘录模式,通过备忘录可以实现时光旅行,返回历史上任何一个状态。

状态模式有一个基本 trait State和方法可以进行状态转换play:stop

pub trait State {
    fn play(self: Box<Self>, player: &mut Player) -> Box<dyn State>;
    fn stop(self: Box<Self>, player: &mut Player) -> Box<dyn State>;
}

next并且prev不更改状态,在impl dyn State中无法覆盖的单独块中可默认实现:

impl dyn State {
    pub fn next(self: Box<Self>, player: &mut Player) -> Box<dyn State> {
        self
    }

    pub fn prev(self: Box<Self>, player: &mut Player) -> Box<dyn State> {
        self
    }
}


每个状态都是实现的类型trait State:

pub struct StoppedState;
pub struct PausedState;
pub struct PlayingState;

impl State for StoppedState {
    ...
}

impl State for PausedState {
    ...
}

在这些实现状态再完成play动作,如PlayingState代码:

fn play(self: Box<Self>, player: &mut Player) -> Box<dyn State> {
    player.pause();

    // Playing -> Paused.
    Box::new(PausedState)
}

这里用特殊self: Box<Self>符号定义。

  • 首先,self不是一个引用,这意味着该方法是一种 "一次性的",它消耗了self并交换到另一个返回Box<dyn State>的状态。
  • 其次,该方法消耗的是Box<dyn State>这样的Box对象,而不是像PlayingState这样的具体类型的对象,因为具体状态在编译时是未知的。

最后,调用相同的操作play会根据调用的位置转换到不同的状态:

let state = Box::new(StoppedState);   // StoppedState.
let state = state.play(&mut player);  // StoppedState -> PlayingState.
let state = state.play(&mut player);  // PlayingState -> PausedState.


什么是Rust语言的类型状态Typestate模式?