文章目录
代码地址: https://gitee.com/lymgoforIT/golang-trick/tree/master/15-fsm
一:状态模式简介
状态模式(State Pattern)
也叫作状态机模式(State Machine Pattern)
状态模式允许对象的内部状态发生改变时,改变它的行为,就好像对象看起来修改了它实例化的类,状态模式是一种对象行为型模式。
状态模式用于解决系统中复杂对象的状态转换以及不同状态下行为的封装问题。当系统中某个对象存在多个状态,这些状态之间可以进行转换,而且对象在不同状态下行为不相同时可以使用状态模式,把特定于状态的代码抽象到一组独立的状态类中避免过多的状态条件判断,减少维护成本。
状态模式的结构十分简单清晰,主要由环境类角色、抽象状态角色和具体状态角色,三个角色构成。
Context(环境类)
:环境类又称为上下文类,它定义客户端需要的接口,内部维护一个当前状态实例,并负责具体状态的切换。State(抽象状态
):定义状态下的行为,可以有一个或多个行为。ConcreteState(具体状态)
:每一个具体状态类对应环境的一个具体状态,不同的具体状态类其行为有所不同。
二: 举例(简单审批流)
在企业内部或者是学校我们经常会看到很多审批流程, 假设我们有一个报销的流程:
员工提交报销申请 -> 直属部门领导审批 -> 财务审批 -> 结束
在这个审批流中,处在不同的环节就是不同的状态,而流程的审批、驳回就是不同的事件
注意看代码中的注释
// Package state 状态模式
package state
import "fmt"
// Machine 状态机(环境类,包含了抽象状态接口,实际工作中还会包含很多其他属性)
type Machine struct {
state IState
}
// SetState 更新状态
func (m *Machine) SetState(state IState) {
m.state = state
}
// GetStateName 获取当前状态
func (m *Machine) GetStateName() string {
return m.state.GetName()
}
// 实际调用的是内部具体状态类的方法
func (m *Machine) Approval() {
m.state.Approval(m)
}
func (m *Machine) Reject() {
m.state.Reject(m)
}
// IState 状态(抽象状态)
type IState interface {
// 审批通过
Approval(m *Machine)
// 驳回
Reject(m *Machine)
// 获取当前状态名称
GetName() string
}
// leaderApproveState 直属领导审批(具体状态类)
type leaderApproveState struct{}
// Approval 获取状态名字 注意:参数是*Machine,因为要改变Machine内的state字段,所以可以理解为machine和状态类是互相依赖的
func (leaderApproveState) Approval(m *Machine) {
fmt.Println("leader 审批成功")
m.SetState(GetFinanceApproveState()) // 领导审批通过后,状态流转到财务审批
}
// GetName 获取状态名字
func (leaderApproveState) GetName() string {
return "LeaderApproveState"
}
// Reject 获取状态名字
func (leaderApproveState) Reject(m *Machine) {}
func GetLeaderApproveState() IState {
return &leaderApproveState{}
}
// financeApproveState 财务审批
type financeApproveState struct{}
// Approval 审批通过
func (f financeApproveState) Approval(m *Machine) {
fmt.Println("财务审批成功")
fmt.Println("触发打款操作")
}
// 拒绝
func (f financeApproveState) Reject(m *Machine) {
m.SetState(GetLeaderApproveState())
}
// GetName 获取名字
func (f financeApproveState) GetName() string {
return "FinanceApproveState"
}
// GetFinanceApproveState GetFinanceApproveState
func GetFinanceApproveState() IState {
return &financeApproveState{}
}
单元测试:
package state
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestMachine_GetStateName(t *testing.T) {
m := &Machine{state: GetLeaderApproveState()} // 设置初始状态为需要领导审批
assert.Equal(t, "LeaderApproveState", m.GetStateName())
m.Approval() // 领导审批通过,打印 leader 审批成功
assert.Equal(t, "FinanceApproveState", m.GetStateName())
m.Reject() // 财务审批拒绝,状态退回到需要领导审批
assert.Equal(t, "LeaderApproveState", m.GetStateName())
m.Approval() // 领导审批通过,打印 leader 审批成功
assert.Equal(t, "FinanceApproveState", m.GetStateName())
m.Approval() // 财务审批通过,打印 财务审批成功
}
测试结果: