状态模式(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,它是每个具体状态实现的抽象,例如SmallMario
、SuperMario
、CapeMario
和FireMario
。并根据具体状态的行为进行相应的实现。而 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)以及优点,使得增加更多的状态是变得轻松简单,同时有利与我们更好的维护它们之间的关系,提升代码的可维护性和可扩展性。
参考
- 【设计模式之美】item.jd.com/13174161.ht...
- 【Head First 设计模式】
- 【gof】 github.com/qiualiang/g...
- 【Wiki】zh.wikipedia.org/wiki/%E6%9C...