Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions GFramework.Core.Abstractions/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ GFramework 框架的抽象层定义模块,包含所有核心组件的接口定
- 事件系统接口 (IEvent, IEventBus)
- 依赖注入容器接口 (IIocContainer)
- 可绑定属性接口 (IBindableProperty)
- 状态管理接口 (IStore, IReducer, IStateSelector, IStoreBuilder)
- 日志系统接口 (ILogger)

## 设计原则
Expand Down
40 changes: 40 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IReadonlyStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using GFramework.Core.Abstractions.Events;

namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 只读状态容器接口,用于暴露应用状态快照和订阅能力。
/// 该抽象适用于 Controller、Query、ViewModel 等只需要观察状态的调用方,
/// 使其无需依赖写入能力即可响应复杂状态树的变化。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public interface IReadonlyStore<out TState>
{
/// <summary>
/// 获取当前状态快照。
/// Store 负责保证返回值与最近一次成功分发后的状态一致。
/// </summary>
TState State { get; }

/// <summary>
/// 订阅状态变化通知。
/// 仅当 Store 判断状态发生有效变化时,才会调用该监听器。
/// </summary>
/// <param name="listener">状态变化时的监听器,参数为新的状态快照。</param>
/// <returns>用于取消订阅的句柄。</returns>
IUnRegister Subscribe(Action<TState> listener);

/// <summary>
/// 订阅状态变化通知,并立即以当前状态调用一次监听器。
/// 该方法适合在 UI 初始化或 ViewModel 首次绑定时建立同步视图。
/// </summary>
/// <param name="listener">状态变化时的监听器,参数为新的状态快照。</param>
/// <returns>用于取消订阅的句柄。</returns>
IUnRegister SubscribeWithInitValue(Action<TState> listener);

/// <summary>
/// 取消订阅指定的状态监听器。
/// </summary>
/// <param name="listener">需要移除的监听器。</param>
void UnSubscribe(Action<TState> listener);
}
19 changes: 19 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IReducer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 定义状态归约器接口。
/// Reducer 应保持纯函数风格:根据当前状态和 action 计算下一状态,
/// 不直接产生副作用,也不依赖外部可变环境。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
/// <typeparam name="TAction">当前 reducer 处理的 action 类型。</typeparam>
public interface IReducer<TState, in TAction>
{
/// <summary>
/// 根据当前状态和 action 计算下一状态。
/// </summary>
/// <param name="currentState">当前状态快照。</param>
/// <param name="action">触发本次归约的 action。</param>
/// <returns>归约后的下一状态。</returns>
TState Reduce(TState currentState, TAction action);
}
17 changes: 17 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IStateSelector.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 定义状态选择器接口,用于从整棵状态树中投影出局部状态视图。
/// 该抽象适用于复用复杂选择逻辑,避免在 UI 或 Controller 中重复编写投影代码。
/// </summary>
/// <typeparam name="TState">源状态类型。</typeparam>
/// <typeparam name="TSelected">投影后的局部状态类型。</typeparam>
public interface IStateSelector<in TState, out TSelected>
{
/// <summary>
/// 从给定状态中选择目标片段。
/// </summary>
/// <param name="state">当前完整状态。</param>
/// <returns>投影后的局部状态。</returns>
TSelected Select(TState state);
}
17 changes: 17 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 可写状态容器接口,提供统一的状态分发入口。
/// 所有状态变更都应通过分发 action 触发,以保持单向数据流和可测试性。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public interface IStore<out TState> : IReadonlyStore<TState>
{
/// <summary>
/// 分发一个 action 以触发状态演进。
/// Store 会按注册顺序执行与该 action 类型匹配的 reducer,并在状态变化后通知订阅者。
/// </summary>
/// <typeparam name="TAction">action 的具体类型。</typeparam>
/// <param name="action">要分发的 action 实例。</param>
void Dispatch<TAction>(TAction action);
}
46 changes: 46 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IStoreBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 定义 Store 构建器接口,用于在创建 Store 之前完成 reducer、中间件和比较器配置。
/// 该抽象适用于模块化注册、依赖注入装配和测试工厂,避免调用方必须依赖具体 Store 类型进行配置。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public interface IStoreBuilder<TState>
{
/// <summary>
/// 配置用于判断状态是否真正变化的比较器。
/// </summary>
/// <param name="comparer">状态比较器。</param>
/// <returns>当前构建器实例。</returns>
IStoreBuilder<TState> WithComparer(IEqualityComparer<TState> comparer);

/// <summary>
/// 添加一个强类型 reducer。
/// </summary>
/// <typeparam name="TAction">当前 reducer 处理的 action 类型。</typeparam>
/// <param name="reducer">要添加的 reducer。</param>
/// <returns>当前构建器实例。</returns>
IStoreBuilder<TState> AddReducer<TAction>(IReducer<TState, TAction> reducer);

/// <summary>
/// 使用委托快速添加一个 reducer。
/// </summary>
/// <typeparam name="TAction">当前 reducer 处理的 action 类型。</typeparam>
/// <param name="reducer">执行归约的委托。</param>
/// <returns>当前构建器实例。</returns>
IStoreBuilder<TState> AddReducer<TAction>(Func<TState, TAction, TState> reducer);

/// <summary>
/// 添加一个 Store 中间件。
/// </summary>
/// <param name="middleware">要添加的中间件。</param>
/// <returns>当前构建器实例。</returns>
IStoreBuilder<TState> UseMiddleware(IStoreMiddleware<TState> middleware);

/// <summary>
/// 基于给定初始状态创建一个新的 Store。
/// </summary>
/// <param name="initialState">Store 的初始状态。</param>
/// <returns>已应用当前构建器配置的 Store 实例。</returns>
IStore<TState> Build(TState initialState);
}
31 changes: 31 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IStoreDiagnostics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 暴露 Store 的诊断信息。
/// 该接口用于调试、监控和后续时间旅行能力的扩展,不参与状态写入流程。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public interface IStoreDiagnostics<TState>
{
/// <summary>
/// 获取当前已注册的订阅者数量。
/// </summary>
int SubscriberCount { get; }

/// <summary>
/// 获取最近一次分发的 action 类型。
/// 即使该次分发未引起状态变化,该值也会更新。
/// </summary>
Type? LastActionType { get; }

/// <summary>
/// 获取最近一次真正改变状态的时间戳。
/// 若尚未发生状态变化,则返回 <see langword="null"/>。
/// </summary>
DateTimeOffset? LastStateChangedAt { get; }

/// <summary>
/// 获取最近一次分发记录。
/// </summary>
StoreDispatchRecord<TState>? LastDispatchRecord { get; }
}
19 changes: 19 additions & 0 deletions GFramework.Core.Abstractions/StateManagement/IStoreMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 定义 Store 分发中间件接口。
/// 中间件用于在 action 分发前后插入日志、诊断、审计或拦截逻辑,
/// 同时保持核心 Store 实现专注于状态归约与订阅通知。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public interface IStoreMiddleware<TState>
{
/// <summary>
/// 执行一次分发管线节点。
/// 实现通常应调用 <paramref name="next"/> 继续后续处理;若选择短路,
/// 需要自行保证上下文状态对调用方仍然是可解释的。
/// </summary>
/// <param name="context">当前分发上下文。</param>
/// <param name="next">继续执行后续中间件或 reducer 的委托。</param>
void Invoke(StoreDispatchContext<TState> context, Action next);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 表示一次 Store 分发流程中的上下文数据。
/// 中间件和 Store 实现通过该对象共享当前 action、分发时间以及归约结果。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public sealed class StoreDispatchContext<TState>
{
/// <summary>
/// 初始化一个新的分发上下文。
/// </summary>
/// <param name="action">当前分发的 action。</param>
/// <param name="previousState">分发前的状态快照。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="action"/> 为 <see langword="null"/> 时抛出。</exception>
public StoreDispatchContext(object action, TState previousState)
{
Action = action ?? throw new ArgumentNullException(nameof(action));
PreviousState = previousState;
NextState = previousState;
DispatchedAt = DateTimeOffset.UtcNow;
}

/// <summary>
/// 获取当前分发的 action 实例。
/// </summary>
public object Action { get; }

/// <summary>
/// 获取当前分发的 action 运行时类型。
/// </summary>
public Type ActionType => Action.GetType();

/// <summary>
/// 获取分发前的状态快照。
/// </summary>
public TState PreviousState { get; }

/// <summary>
/// 获取或设置归约后的下一状态。
/// Store 会在 reducer 执行完成后使用该值更新内部状态。
/// </summary>
public TState NextState { get; set; }

/// <summary>
/// 获取或设置本次分发是否导致状态发生变化。
/// 中间件可读取该值进行日志和诊断,但通常应由 Store 负责最终判定。
/// </summary>
public bool HasStateChanged { get; set; }

/// <summary>
/// 获取本次分发创建时的时间戳。
/// </summary>
public DateTimeOffset DispatchedAt { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace GFramework.Core.Abstractions.StateManagement;

/// <summary>
/// 记录最近一次 Store 分发的结果。
/// 该结构为调试和诊断提供稳定的只读视图,避免调用方直接依赖 Store 的内部状态。
/// </summary>
/// <typeparam name="TState">状态树的根状态类型。</typeparam>
public sealed class StoreDispatchRecord<TState>
{
/// <summary>
/// 初始化一条分发记录。
/// </summary>
/// <param name="action">本次分发的 action。</param>
/// <param name="previousState">分发前状态。</param>
/// <param name="nextState">分发后状态。</param>
/// <param name="hasStateChanged">是否发生了有效状态变化。</param>
/// <param name="dispatchedAt">分发时间。</param>
/// <exception cref="ArgumentNullException">当 <paramref name="action"/> 为 <see langword="null"/> 时抛出。</exception>
public StoreDispatchRecord(
object action,
TState previousState,
TState nextState,
bool hasStateChanged,
DateTimeOffset dispatchedAt)
{
Action = action ?? throw new ArgumentNullException(nameof(action));
PreviousState = previousState;
NextState = nextState;
HasStateChanged = hasStateChanged;
DispatchedAt = dispatchedAt;
}

/// <summary>
/// 获取本次分发的 action 实例。
/// </summary>
public object Action { get; }

/// <summary>
/// 获取本次分发的 action 运行时类型。
/// </summary>
public Type ActionType => Action.GetType();

/// <summary>
/// 获取分发前状态。
/// </summary>
public TState PreviousState { get; }

/// <summary>
/// 获取分发后状态。
/// </summary>
public TState NextState { get; }

/// <summary>
/// 获取本次分发是否产生了有效状态变化。
/// </summary>
public bool HasStateChanged { get; }

/// <summary>
/// 获取分发时间。
/// </summary>
public DateTimeOffset DispatchedAt { get; }
}
Loading
Loading