状态机无处不在,像咱们经常使用的 tcp、http、regexp 等等本质上都是状态机。设计模式
状态机是由状态和状态间的转换构成的。app
拿红绿灯来举个简单的例子,红绿灯会处于 3 个状态:红、绿、黄,这几个状态之间有肯定的转换路径:tcp
+-----------+ +------------+ +---------+ | Green +----->+ Yellow +----->+ Red | +-----+-----+ +------------+ +----+----+ ^ | | | +-------------------------------------+
若是用 rust 来写的话,我可能会这样实现:oop
use std::thread::sleep; use core::borrow::Borrow; use std::time::Duration; // 把红绿灯当作一个状态机,状态转换过程以下: //+-----------+ +------------+ +---------+ //| Green +----->+ Yellow +----->+ Red | //+-----+-----+ +------------+ +----+----+ // ^ | // | | // +-------------------------------------+ #[derive(Debug)] enum TrafficLightState { Red { waiting_time: std::time::Duration }, Yellow { waiting_time: std::time::Duration }, Green { waiting_time: std::time::Duration }, } struct TrafficLight { state: TrafficLightState, } fn change_light(mut state: &TrafficLightState) -> TrafficLightState { match state { TrafficLightState::Green { waiting_time } => { sleep(*waiting_time); TrafficLightState::Yellow { waiting_time: std::time::Duration::new(10, 0) } }, TrafficLightState::Red { waiting_time } => { sleep(*waiting_time); TrafficLightState::Green { waiting_time: std::time::Duration::new(60, 0) } }, TrafficLightState::Yellow { waiting_time } => { sleep(*waiting_time); TrafficLightState::Red { waiting_time: std::time::Duration::new(60, 0) } } } } fn main() { let mut state_machine = TrafficLight{ state: TrafficLightState::Green { waiting_time: std::time::Duration::new(60, 0) } }; loop { println!("{:?}", state_machine.state); state_machine.state = change_light(&state_machine.state) } }
初始为绿色状态,60s 后切换为黄色状态,10s 后切换为红色状态,60s 后又切换回绿色,如此循环往复。设计
这段代码虽然实现了咱们的需求,可是并非很漂亮。除了存在一些重复的代码,更严重的问题是状态的变换彻底暴露给了外部,这意味着外部能够作任意的状态切换,好比红色 -> 黄色,黄色 -> 绿色 等等,这并非咱们但愿的行为。并且从职责分离的角度来看,状态的切换也应该是由一个状态机根据当前的输入和一些环境条件自行决定的,这些行为不须要也不该该让外部知道。code
如今咱们再来描述一下咱们想要实现的状态机:regexp
impl TrafficLight { fn new() -> Self { TrafficLight { state: TrafficLightState::Green { waiting_time: std::time::Duration::new(60, 0) } } } fn change_light(&mut self) { self.state = match self.state { TrafficLightState::Green { waiting_time } => { sleep(waiting_time); TrafficLightState::Yellow { waiting_time: std::time::Duration::new(10, 0) } }, TrafficLightState::Red { waiting_time } => { sleep(waiting_time); TrafficLightState::Green { waiting_time: std::time::Duration::new(60, 0) } }, TrafficLightState::Yellow { waiting_time } => { sleep(waiting_time); TrafficLightState::Red { waiting_time: std::time::Duration::new(60, 0) } } } } }
咱们使用 rust 的关键字 impl 为类型 TrafficLight 关联了 new 方法和 change_light 方法。以后外部就能够直接调用 new 来进行初始化了,而修改状态只须要调用 change_light 就能够了。get
面对复杂状态的转换时,咱们最好能把全部的状态和变换路径都描述出来。咱们能够利用泛型来描述多种状态,并使用 From 和 Into 来描述状态间的变换。it
use std::thread::sleep; use core::borrow::Borrow; use std::time::Duration; use std::cmp::Ordering::Greater; // 把红绿灯当作一个状态机,状态转换过程以下: //+-----------+ +------------+ +---------+ //| Green +----->+ Yellow +----->+ Red | //+-----+-----+ +------------+ +----+----+ // ^ | // | | // +-------------------------------------+ #[derive(Debug)] struct Red { wait_time: Duration } impl Red { fn new() -> Self { Red{ wait_time: Duration::new(60, 0) } } } #[derive(Debug)] struct Green { wait_time: std::time::Duration } impl Green { fn new() -> Self { Green{ wait_time: Duration::new(60, 0) } } } #[derive(Debug)] struct Yellow { wait_time: std::time::Duration } impl Yellow { fn new() -> Self { Yellow{ wait_time: Duration::new(10, 0) } } } #[derive(Debug)] struct TrafficLight<TLS> { state: TLS, } impl TrafficLight<Green> { fn new() -> Self { TrafficLight { state: Green::new(), } } } impl From<TrafficLight<Green>> for TrafficLight<Yellow> { fn from(green: TrafficLight<Green>) -> TrafficLight<Yellow> { println!("last state is {:?}", green); sleep(green.state.wait_time); TrafficLight { state: Yellow::new(), } } } impl From<TrafficLight<Yellow>> for TrafficLight<Red> { fn from(yellow: TrafficLight<Yellow>) -> TrafficLight<Red> { println!("last state is {:?}", yellow); sleep(yellow.state.wait_time); TrafficLight { state: Red::new(), } } } impl From<TrafficLight<Red>> for TrafficLight<Green> { fn from(red: TrafficLight<Red>) -> TrafficLight<Green> { println!("last state is {:?}", red); sleep(red.state.wait_time); TrafficLight { state: Green::new(), } } } enum TrafficLightWrapper { Red(TrafficLight<Red>), Green(TrafficLight<Green>), Yellow(TrafficLight<Yellow>), } impl TrafficLightWrapper { fn new() -> Self { TrafficLightWrapper::Green(TrafficLight::new()) } fn step(mut self) -> Self { match self { TrafficLightWrapper::Green(green) => TrafficLightWrapper::Yellow(green.into()), TrafficLightWrapper::Yellow(yellow) => TrafficLightWrapper::Red(yellow.into()), TrafficLightWrapper::Red(red) => TrafficLightWrapper::Green(red.into()) } } } fn main() { let mut state_machine = TrafficLightWrapper::new(); loop { state_machine = state_machine.step(); } }
把上面的代码抽象一下,咱们就能获得状态机设计模式,以后解决相似问题,就能够直接利用这个模型了:io
嗯,Rust 真香!