mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 04:59:01 +08:00
refactor(state): 重构状态机实现以支持状态历史记录和线程安全
- 在销毁时添加当前状态退出和所有状态清理逻辑 - 向IStateMachine接口添加状态检查、获取、历史记录等新方法 - 实现线程安全的状态机,添加锁保护并发访问 - 添加状态历史记录功能,支持最大历史数量限制 - 实现GoBack状态回退功能和状态转换验证 - 添加状态切换前后的回调方法 - 在注销状态时从历史记录中移除相关引用 - 添加Unregister方法中的状态转换验证逻辑
This commit is contained in:
parent
442e8e7088
commit
b6554c5820
@ -1,4 +1,7 @@
|
|||||||
namespace GFramework.Core.Abstractions.state;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace GFramework.Core.Abstractions.state;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 状态机接口,用于管理状态的注册、切换和验证
|
/// 状态机接口,用于管理状态的注册、切换和验证
|
||||||
@ -34,4 +37,47 @@ public interface IStateMachine
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="T">要切换到的状态类型,必须实现IState接口</typeparam>
|
/// <typeparam name="T">要切换到的状态类型,必须实现IState接口</typeparam>
|
||||||
void ChangeTo<T>() where T : IState;
|
void ChangeTo<T>() where T : IState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查指定类型的状态是否已注册
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">要检查的状态类型</typeparam>
|
||||||
|
/// <returns>如果状态已注册则返回true,否则返回false</returns>
|
||||||
|
bool IsRegistered<T>() where T : IState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定类型的已注册状态实例
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">要获取的状态类型</typeparam>
|
||||||
|
/// <returns>如果状态存在则返回对应实例,否则返回null</returns>
|
||||||
|
T? GetState<T>() where T : class, IState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有已注册状态的类型集合
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>包含所有已注册状态类型的枚举器</returns>
|
||||||
|
IEnumerable<Type> GetRegisteredStateTypes();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取上一个状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>如果历史记录存在则返回上一个状态,否则返回null</returns>
|
||||||
|
IState? GetPreviousState();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取状态历史记录
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>状态历史记录的只读副本</returns>
|
||||||
|
IReadOnlyList<IState> GetStateHistory();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回退到上一个状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>如果成功回退则返回true,否则返回false</returns>
|
||||||
|
bool GoBack();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空状态历史记录
|
||||||
|
/// </summary>
|
||||||
|
void ClearHistory();
|
||||||
}
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using GFramework.Core.Abstractions.enums;
|
using System;
|
||||||
|
using GFramework.Core.Abstractions.enums;
|
||||||
using GFramework.Core.architecture;
|
using GFramework.Core.architecture;
|
||||||
using GFramework.Core.Tests.architecture;
|
using GFramework.Core.Tests.architecture;
|
||||||
using GFramework.Core.Tests.events;
|
using GFramework.Core.Tests.events;
|
||||||
|
|||||||
@ -61,6 +61,20 @@ public class ContextAwareStateMachine : StateMachine, ISystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual void Destroy()
|
public virtual void Destroy()
|
||||||
{
|
{
|
||||||
|
// 退出当前状态
|
||||||
|
if (Current != null)
|
||||||
|
{
|
||||||
|
Current.OnExit(null);
|
||||||
|
Current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理所有状态
|
||||||
|
foreach (var state in States.Values.OfType<IDisposable>())
|
||||||
|
{
|
||||||
|
state.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
States.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -5,8 +5,11 @@ namespace GFramework.Core.state;
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 状态机实现类,用于管理状态的注册、切换和生命周期
|
/// 状态机实现类,用于管理状态的注册、切换和生命周期
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StateMachine : IStateMachine
|
public class StateMachine(int maxHistorySize = 10) : IStateMachine
|
||||||
{
|
{
|
||||||
|
private readonly object _lock = new();
|
||||||
|
private readonly Stack<IState> _stateHistory = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存储所有已注册状态的字典,键为状态类型,值为状态实例
|
/// 存储所有已注册状态的字典,键为状态类型,值为状态实例
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -15,14 +18,19 @@ public class StateMachine : IStateMachine
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前激活的状态
|
/// 获取当前激活的状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IState? Current { get; private set; }
|
public IState? Current { get; protected set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册一个状态到状态机中
|
/// 注册一个状态到状态机中
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="state">要注册的状态实例</param>
|
/// <param name="state">要注册的状态实例</param>
|
||||||
public void Register(IState state)
|
public void Register(IState state)
|
||||||
=> States[state.GetType()] = state;
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
States[state.GetType()] = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从状态机中注销指定类型的状态
|
/// 从状态机中注销指定类型的状态
|
||||||
@ -30,17 +38,28 @@ public class StateMachine : IStateMachine
|
|||||||
/// <typeparam name="T">要注销的状态类型</typeparam>
|
/// <typeparam name="T">要注销的状态类型</typeparam>
|
||||||
public void Unregister<T>() where T : IState
|
public void Unregister<T>() where T : IState
|
||||||
{
|
{
|
||||||
var type = typeof(T);
|
lock (_lock)
|
||||||
if (!States.TryGetValue(type, out var state)) return;
|
|
||||||
|
|
||||||
// 如果当前状态是要注销的状态,则先执行退出逻辑
|
|
||||||
if (Current == state)
|
|
||||||
{
|
{
|
||||||
Current.OnExit(null);
|
var type = typeof(T);
|
||||||
Current = null;
|
if (!States.TryGetValue(type, out var state)) return;
|
||||||
}
|
|
||||||
|
|
||||||
States.Remove(type);
|
// 如果当前状态是要注销的状态,则先执行退出逻辑
|
||||||
|
if (Current == state)
|
||||||
|
{
|
||||||
|
Current.OnExit(null);
|
||||||
|
Current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从历史记录中移除该状态的所有引用
|
||||||
|
var tempStack = new Stack<IState>(_stateHistory.Reverse());
|
||||||
|
_stateHistory.Clear();
|
||||||
|
foreach (var historyState in tempStack.Where(s => s != state))
|
||||||
|
{
|
||||||
|
_stateHistory.Push(historyState);
|
||||||
|
}
|
||||||
|
|
||||||
|
States.Remove(type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -63,10 +82,117 @@ public class StateMachine : IStateMachine
|
|||||||
/// <exception cref="InvalidOperationException">当目标状态未注册时抛出</exception>
|
/// <exception cref="InvalidOperationException">当目标状态未注册时抛出</exception>
|
||||||
public void ChangeTo<T>() where T : IState
|
public void ChangeTo<T>() where T : IState
|
||||||
{
|
{
|
||||||
if (!States.TryGetValue(typeof(T), out var target))
|
lock (_lock)
|
||||||
throw new InvalidOperationException("State not registered.");
|
{
|
||||||
|
// 检查目标状态是否已注册
|
||||||
|
if (!States.TryGetValue(typeof(T), out var target))
|
||||||
|
throw new InvalidOperationException("State not registered.");
|
||||||
|
|
||||||
ChangeInternal(target);
|
// 验证当前状态是否可以转换到目标状态
|
||||||
|
if (Current != null && !Current.CanTransitionTo(target))
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
$"Cannot transition from {Current.GetType().Name} to {typeof(T).Name}");
|
||||||
|
|
||||||
|
ChangeInternal(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查指定类型的状态是否已注册
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">要检查的状态类型</typeparam>
|
||||||
|
/// <returns>如果状态已注册则返回true,否则返回false</returns>
|
||||||
|
public bool IsRegistered<T>() where T : IState => States.ContainsKey(typeof(T));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取指定类型的已注册状态实例
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">要获取的状态类型</typeparam>
|
||||||
|
/// <returns>如果状态存在则返回对应实例,否则返回null</returns>
|
||||||
|
public T? GetState<T>() where T : class, IState => States.TryGetValue(typeof(T), out var state) ? state as T : null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有已注册状态的类型集合
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>包含所有已注册状态类型的枚举器</returns>
|
||||||
|
public IEnumerable<Type> GetRegisteredStateTypes() => States.Keys;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取上一个状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>如果历史记录存在则返回上一个状态,否则返回null</returns>
|
||||||
|
public IState? GetPreviousState()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _stateHistory.Count > 0 ? _stateHistory.Peek() : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取状态历史记录
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>状态历史记录的只读副本,从最近到最远排序</returns>
|
||||||
|
public IReadOnlyList<IState> GetStateHistory()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
return _stateHistory.ToList().AsReadOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 回退到上一个状态
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>如果成功回退则返回true,否则返回false</returns>
|
||||||
|
public bool GoBack()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if (_stateHistory.Count == 0) return false;
|
||||||
|
|
||||||
|
var previousState = _stateHistory.Pop();
|
||||||
|
|
||||||
|
// 检查上一个状态是否仍然注册
|
||||||
|
if (!States.ContainsValue(previousState))
|
||||||
|
{
|
||||||
|
// 如果状态已被注销,继续尝试更早的状态
|
||||||
|
return GoBack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 回退时不添加到历史记录
|
||||||
|
ChangeInternalWithoutHistory(previousState);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空状态历史记录
|
||||||
|
/// </summary>
|
||||||
|
public void ClearHistory()
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_stateHistory.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 内部状态切换方法(不记录历史),用于回退操作
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="next">下一个状态实例</param>
|
||||||
|
protected virtual void ChangeInternalWithoutHistory(IState next)
|
||||||
|
{
|
||||||
|
if (Current == next) return;
|
||||||
|
|
||||||
|
var old = Current;
|
||||||
|
OnStateChanging(old, next);
|
||||||
|
|
||||||
|
old?.OnExit(next);
|
||||||
|
Current = next;
|
||||||
|
Current.OnEnter(old);
|
||||||
|
|
||||||
|
OnStateChanged(old, Current);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -75,13 +201,68 @@ public class StateMachine : IStateMachine
|
|||||||
/// <param name="next">下一个状态实例</param>
|
/// <param name="next">下一个状态实例</param>
|
||||||
protected virtual void ChangeInternal(IState next)
|
protected virtual void ChangeInternal(IState next)
|
||||||
{
|
{
|
||||||
|
// 检查是否为相同状态,避免不必要的切换
|
||||||
if (Current == next) return;
|
if (Current == next) return;
|
||||||
if (Current != null && !Current.CanTransitionTo(next)) return;
|
|
||||||
|
// 验证当前状态是否允许切换到目标状态
|
||||||
|
if (Current != null && !Current.CanTransitionTo(next))
|
||||||
|
{
|
||||||
|
OnTransitionRejected(Current, next);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var old = Current;
|
var old = Current;
|
||||||
old?.OnExit(next);
|
OnStateChanging(old, next);
|
||||||
|
|
||||||
|
// 将当前状态添加到历史记录
|
||||||
|
if (Current != null)
|
||||||
|
{
|
||||||
|
_stateHistory.Push(Current);
|
||||||
|
|
||||||
|
// 限制历史记录大小
|
||||||
|
if (_stateHistory.Count > maxHistorySize)
|
||||||
|
{
|
||||||
|
// 移除最旧的记录(栈底元素)
|
||||||
|
var tempStack = new Stack<IState>(_stateHistory.Reverse().Skip(1));
|
||||||
|
_stateHistory.Clear();
|
||||||
|
foreach (var state in tempStack.Reverse())
|
||||||
|
{
|
||||||
|
_stateHistory.Push(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
old?.OnExit(next);
|
||||||
Current = next;
|
Current = next;
|
||||||
Current.OnEnter(old);
|
Current.OnEnter(old);
|
||||||
|
|
||||||
|
OnStateChanged(old, Current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当状态转换被拒绝时的回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">源状态</param>
|
||||||
|
/// <param name="to">目标状态</param>
|
||||||
|
protected virtual void OnTransitionRejected(IState from, IState to)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当状态即将发生改变时的回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">源状态</param>
|
||||||
|
/// <param name="to">目标状态</param>
|
||||||
|
protected virtual void OnStateChanging(IState? from, IState to)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当状态改变完成后的回调方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="from">源状态</param>
|
||||||
|
/// <param name="to">目标状态</param>
|
||||||
|
protected virtual void OnStateChanged(IState? from, IState? to)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user