设计模式-状态模式与Go实现FSM(有限状态机)

状态模式(State)

允许一个对象在其内部状态发生改变时改变其行为

> gof

状态模式它将对象的行为封装在不同的状态类中,并使用接口来定义状态之间的转换。这样,当对象的状态改变时,它可以在不同的状态类之间切换,并执行相应的行为。状态模式适用于实现状态机,而状态机常用在游戏、工作流引擎等系统开发中。

Go实现FSM

有限状态机 (英语:finite-state machine,缩写FSM )又称有限状态自动机 (英语:finite-state automaton,缩写FSA ),简称状态机 ,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学计算模型

> Wiki

状态机包含了以下几个元素:状态(State)、事件(Event)、动作(Action)。接着,我们通过Go语言实现一个FSM来演示如何状态模式。

以游戏马里奥为例,其中Mario吃到不同的道具会变化成超级马里奥(Super Mario)、火焰马里奥(Fire Mario)等, 即不同的状态。不同的马里奥有不同的技能,即不同的动作。

FSM实现1 :switch-case 分支逻辑法

顾名思义,罗列出每个状态,详细如下:

go 复制代码
import "fmt"

type State string

// 定义状态常量
const (
    SMALL State = "SMALL"
    SUPER State = "SUPER"
    CAPE  State = "CAPE"
    FIRE  State = "FIRE"
)

type MarioStateMachine struct {
    score        int
    currentState State
}

func NewMarioStateMachine() *MarioStateMachine {
    return &MarioStateMachine{
       score:        0,
       currentState: SMALL,
    }
}

func (m *MarioStateMachine) obtainMushRoom() {
    if m.currentState == SMALL {
       m.currentState = SUPER
       m.score += 100
    }
}

func (m *MarioStateMachine) obtainCape() {
    if m.currentState == SMALL || m.currentState == SUPER {
       m.currentState = CAPE
       m.score += 200
    }
}

func (m *MarioStateMachine) obtainFireFlower() {
    if m.currentState == SMALL || m.currentState == SUPER {
       m.currentState = FIRE
       m.score += 300
    }
}

func (m *MarioStateMachine) meetMonster() {
    if m.currentState == SUPER {
       m.currentState = SMALL
       m.score -= 100
       return
    }

    if m.currentState == CAPE {
       m.currentState = SMALL
       m.score -= 200
       return
    }

    if m.currentState == FIRE {
       m.currentState = SMALL
       m.score -= 300
       return
    }
}

func (m *MarioStateMachine) getScore() int {
    return m.score
}

func (m *MarioStateMachine) getCurrentState() State {
    return m.currentState
}

func main() {
    mario := NewMarioStateMachine()
    fmt.Println("Initial state:", mario.getCurrentState())
    fmt.Println("Initial score:", mario.getScore())

    mario.obtainMushRoom()
    fmt.Println("State after obtaining mushroom:", mario.getCurrentState())
    fmt.Println("Score after obtaining mushroom:", mario.getScore())

    mario.obtainCape()
    fmt.Println("State after obtaining cape:", mario.getCurrentState())
    fmt.Println("Score after obtaining cape:", mario.getScore())

    mario.obtainFireFlower()
    fmt.Println("State after obtaining fire flower:", mario.getCurrentState())
    fmt.Println("Score after obtaining fire flower:", mario.getScore())

    mario.meetMonster()
    fmt.Println("State after meeting monster:", mario.getCurrentState())
    fmt.Println("Score after meeting monster:", mario.getScore())
}
//OUTPUT:
//Initial state: SMALL
//Initial score: 0                       
//State after obtaining mushroom: SUPER  
//Score after obtaining mushroom: 100    
//State after obtaining cape: CAPE       
//Score after obtaining cape: 300        
//State after obtaining fire flower: FIRE
//Score after obtaining fire flower: 600 
//State after meeting monster: SMALL     
//Score after meeting monster: 300   

上例中,通过NewMarioStateMachine创建了一个状态机,通过调用不同的method(obtainMushRoom,obtainCape,obtainFireFlower,meetMonster),实现了不同状态的切换,这样编写的代码缺点是会包含大量的 if-else 或 switch-case,于是我们对上例进行优化。

FSM实现2 : 状态模式

首先定义一个IMario interface,它是每个具体状态实现的抽象,例如SmallMarioSuperMarioCapeMarioFireMario。并根据具体状态的行为进行相应的实现。而 MarioStateMachine为状态机,它包含当前Mario状态和其他相关方法来进行状态的转换,详细如下。

go 复制代码
package main

import "fmt"

type State string

// 定义状态常量
const (
    SMALL State = "SMALL"
    SUPER State = "SUPER"
    CAPE  State = "CAPE"
    FIRE  State = "FIRE"
)

// 定义接口
type IMario interface {
    getName() State
    obtainMushRoom()
    obtainCape()
    obtainFireFlower()
    meetMonster()
}

// SmallMario 实现 IMario 接口
type SmallMario struct {
    stateMachine *MarioStateMachine
}

func newSmallMario(stateMachine *MarioStateMachine) *SmallMario {
    return &SmallMario{
       stateMachine: stateMachine,
    }
}

func (m *SmallMario) getName() State {
    return SMALL
}

func (m *SmallMario) obtainMushRoom() {
    m.stateMachine.setCurrentState(newSuperMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() + 100)
}

func (m *SmallMario) obtainCape() {
    //m.stateMachine.setCurrentState(newCapeMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() + 200)
}

func (m *SmallMario) obtainFireFlower() {
    //m.stateMachine.setCurrentState(newFireMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() + 300)
}

func (m *SmallMario) meetMonster() {
    // do nothing
}

// SuperMario 实现 IMario 接口
type SuperMario struct {
    stateMachine *MarioStateMachine
}

func newSuperMario(stateMachine *MarioStateMachine) *SuperMario {
    return &SuperMario{
       stateMachine: stateMachine,
    }
}

func (m *SuperMario) getName() State {
    return SUPER
}

func (m *SuperMario) obtainMushRoom() {
    // do nothing
}

func (m *SuperMario) obtainCape() {
    m.stateMachine.setCurrentState(newCapeMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() + 200)
}

func (m *SuperMario) obtainFireFlower() {
    m.stateMachine.setCurrentState(newFireMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() + 300)
}

func (m *SuperMario) meetMonster() {
    m.stateMachine.setCurrentState(newSmallMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() - 100)
}


// CapeMario 实现 IMario 接口
type CapeMario struct {
    stateMachine *MarioStateMachine
}

func newCapeMario(stateMachine *MarioStateMachine) *CapeMario {
    return &CapeMario{
       stateMachine: stateMachine,
    }
}

func (m *CapeMario) getName() State {
    return CAPE
}

func (m *CapeMario) obtainMushRoom() {
    // do nothing
}

func (m *CapeMario) obtainCape() {
    // do nothing
}

func (m *CapeMario) obtainFireFlower() {
    m.stateMachine.setCurrentState(newFireMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() + 300)
}

func (m *CapeMario) meetMonster() {
    m.stateMachine.setCurrentState(newSmallMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() - 200)
}

// FireMario 实现 IMario 接口
type FireMario struct {
    stateMachine *MarioStateMachine
}

func newFireMario(stateMachine *MarioStateMachine) *FireMario {
    return &FireMario{
       stateMachine: stateMachine,
    }
}

func (m *FireMario) getName() State {
    return FIRE
}

func (m *FireMario) obtainMushRoom() {
    // do nothing
}

func (m *FireMario) obtainCape() {
    // do nothing
}

func (m *FireMario) obtainFireFlower() {
    // do nothing
}

func (m *FireMario) meetMonster() {
    m.stateMachine.setCurrentState(newSmallMario(m.stateMachine))
    m.stateMachine.setScore(m.stateMachine.getScore() - 300)
}

// MarioStateMachine 结构体
type MarioStateMachine struct {
    score        int
    currentState IMario
}

func newMarioStateMachine() *MarioStateMachine {
    mario := &MarioStateMachine{
       score: 0,
    }
    mario.currentState = newSmallMario(mario)
    return mario
}

func (m *MarioStateMachine) obtainMushRoom() {
    m.currentState.obtainMushRoom()
}

func (m *MarioStateMachine) obtainCape() {
    m.currentState.obtainCape()
}

func (m *MarioStateMachine) obtainFireFlower() {
    m.currentState.obtainFireFlower()
}

func (m *MarioStateMachine) meetMonster() {
    m.currentState.meetMonster()
}

func (m *MarioStateMachine) getScore() int {
    return m.score
}

func (m *MarioStateMachine) getCurrentState() State {
    return m.currentState.getName()
}

func (m *MarioStateMachine) setScore(score int) {
    m.score = score
}

func (m *MarioStateMachine) setCurrentState(currentState IMario) {
    m.currentState = currentState
}

func main() {
    mario := newMarioStateMachine()
    fmt.Println("Initial state:", mario.getCurrentState())
    fmt.Println("Initial score:", mario.getScore())

    mario.obtainMushRoom()
    fmt.Println("State after obtaining mushroom:", mario.getCurrentState())
    fmt.Println("Score after obtaining mushroom:", mario.getScore())

    mario.obtainCape()
    fmt.Println("State after obtaining cape:", mario.getCurrentState())
    fmt.Println("Score after obtaining cape:", mario.getScore())

    mario.obtainFireFlower()
    fmt.Println("State after obtaining fire flower:", mario.getCurrentState())
    fmt.Println("Score after obtaining fire flower:", mario.getScore())

    mario.meetMonster()
    fmt.Println("State after meeting monster:", mario.getCurrentState())
    fmt.Println("Score after meeting monster:", mario.getScore())
}
//OUTPUT
//Initial state: SMALL
//Initial score: 0
//State after obtaining mushroom: SUPER  
//Score after obtaining mushroom: 100    
//State after obtaining cape: CAPE       
//Score after obtaining cape: 300        
//State after obtaining fire flower: FIRE
//Score after obtaining fire flower: 600 
//State after meeting monster: SMALL     
//Score after meeting monster: 300 

总结

本文简要介绍了状态模式的概念,其将对象的行为封装在不同的状态类中,并使用接口来定义状态之间的转换。状态机是表示有限个[状态]以及在这些状态之间的转移和动作等行为的数学计算模型,状态机常用在游戏、工作流引擎等系统开发中,文中演示了该模式如何实现状态机(FSM)以及优点,使得增加更多的状态是变得轻松简单,同时有利与我们更好的维护它们之间的关系,提升代码的可维护性和可扩展性。

参考

相关推荐
开心就好202518 分钟前
WebView远程调试全景指南:实战对比主流工具优劣与适配场景
后端
用户214118326360223 分钟前
AI 一键搞定!中医药科普短视频制作全流程
后端
SimonKing31 分钟前
告别传统读写!RandomAccessFile让你的Java程序快人一步
java·后端·程序员
hqxstudying2 小时前
Java创建型模式---原型模式
java·开发语言·设计模式·代码规范
蓝倾2 小时前
如何使用Python通过API接口批量抓取小红书笔记评论?
前端·后端·api
aloha_2 小时前
Flowable 引擎在启动时没办法找到AsyncListenableTaskExecutor类型的 bean
后端
保持学习ing2 小时前
day1--项目搭建and内容管理模块
java·数据库·后端·docker·虚拟机
超级小忍3 小时前
服务端向客户端主动推送数据的几种方法(Spring Boot 环境)
java·spring boot·后端
字节跳跃者3 小时前
为什么Java已经不推荐使用Stack了?
javascript·后端
字节跳跃者3 小时前
深入剖析HashMap:理解Hash、底层实现与扩容机制
javascript·后端