23.有限状态机(二)状态模式实现

本文介绍了Go语言中状态模式的应用,通过实例展示如何设计一个简单的审批流程,包括环境类、抽象状态和具体状态的角色,以及状态的转换和事件处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


代码地址: 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() // 财务审批通过,打印  财务审批成功
}


测试结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值