狀態機無處不在,像咱們經常使用的 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 真香!