mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-25 13:33:28 +08:00
docs(core): 添加状态管理文档并完善属性绑定指南
- 新增 state-management 文档,介绍集中式状态容器方案 - 在 property 文档中补充与 Store 的使用边界说明 - 更新核心功能表格,添加状态管理条目链接 - 在 README 中增加 StateManagement 功能描述 - 添加状态管理相关接口到抽象层文档 - 提供 Store 与 BindableProperty 的选择指导原则
This commit is contained in:
parent
2b4b87baba
commit
79f1240e1d
@ -12,6 +12,7 @@ GFramework 框架的抽象层定义模块,包含所有核心组件的接口定
|
|||||||
- 事件系统接口 (IEvent, IEventBus)
|
- 事件系统接口 (IEvent, IEventBus)
|
||||||
- 依赖注入容器接口 (IIocContainer)
|
- 依赖注入容器接口 (IIocContainer)
|
||||||
- 可绑定属性接口 (IBindableProperty)
|
- 可绑定属性接口 (IBindableProperty)
|
||||||
|
- 状态管理接口 (IStore, IReducer, IStateSelector)
|
||||||
- 日志系统接口 (ILogger)
|
- 日志系统接口 (ILogger)
|
||||||
|
|
||||||
## 设计原则
|
## 设计原则
|
||||||
|
|||||||
@ -13,6 +13,7 @@ GFramework 框架的核心模块,提供MVC架构的基础设施。
|
|||||||
- **Events** - 事件系统,实现组件间松耦合通信
|
- **Events** - 事件系统,实现组件间松耦合通信
|
||||||
- **IoC** - 轻量级依赖注入容器
|
- **IoC** - 轻量级依赖注入容器
|
||||||
- **Property** - 可绑定属性,支持数据绑定和响应式编程
|
- **Property** - 可绑定属性,支持数据绑定和响应式编程
|
||||||
|
- **StateManagement** - 集中式状态容器,支持状态归约、选择器和诊断
|
||||||
- **Utility** - 无状态工具类
|
- **Utility** - 无状态工具类
|
||||||
- **Pool** - 对象池系统,减少GC压力
|
- **Pool** - 对象池系统,减少GC压力
|
||||||
- **Extensions** - 框架扩展方法
|
- **Extensions** - 框架扩展方法
|
||||||
|
|||||||
@ -404,24 +404,25 @@ public class PlayerController : IController
|
|||||||
|
|
||||||
## 包说明
|
## 包说明
|
||||||
|
|
||||||
| 包名 | 职责 | 文档 |
|
| 包名 | 职责 | 文档 |
|
||||||
|------------------|-----------------|----------------------|
|
|----------------------|-----------------|--------------------------|
|
||||||
| **architecture** | 架构核心,管理所有组件生命周期 | [查看](./architecture) |
|
| **architecture** | 架构核心,管理所有组件生命周期 | [查看](./architecture) |
|
||||||
| **constants** | 框架常量定义 | 本文档 |
|
| **constants** | 框架常量定义 | 本文档 |
|
||||||
| **model** | 数据模型层,存储状态 | [查看](./model) |
|
| **model** | 数据模型层,存储状态 | [查看](./model) |
|
||||||
| **system** | 业务逻辑层,处理业务规则 | [查看](./system) |
|
| **system** | 业务逻辑层,处理业务规则 | [查看](./system) |
|
||||||
| **controller** | 控制器层,连接视图和逻辑 | (在 Abstractions 中) |
|
| **controller** | 控制器层,连接视图和逻辑 | (在 Abstractions 中) |
|
||||||
| **utility** | 工具类层,提供无状态工具 | [查看](./utility) |
|
| **utility** | 工具类层,提供无状态工具 | [查看](./utility) |
|
||||||
| **command** | 命令模式,封装写操作 | [查看](./command) |
|
| **command** | 命令模式,封装写操作 | [查看](./command) |
|
||||||
| **query** | 查询模式,封装读操作 | [查看](./query) |
|
| **query** | 查询模式,封装读操作 | [查看](./query) |
|
||||||
| **events** | 事件系统,组件间通信 | [查看](./events) |
|
| **events** | 事件系统,组件间通信 | [查看](./events) |
|
||||||
| **property** | 可绑定属性,响应式编程 | [查看](./property) |
|
| **property** | 可绑定属性,响应式编程 | [查看](./property) |
|
||||||
| **ioc** | IoC 容器,依赖注入 | [查看](./ioc) |
|
| **state-management** | 集中式状态容器与选择器 | [查看](./state-management) |
|
||||||
| **rule** | 规则接口,定义组件约束 | [查看](./rule) |
|
| **ioc** | IoC 容器,依赖注入 | [查看](./ioc) |
|
||||||
| **extensions** | 扩展方法,简化 API 调用 | [查看](./extensions) |
|
| **rule** | 规则接口,定义组件约束 | [查看](./rule) |
|
||||||
| **logging** | 日志系统,记录运行日志 | [查看](./logging) |
|
| **extensions** | 扩展方法,简化 API 调用 | [查看](./extensions) |
|
||||||
| **environment** | 环境接口,提供运行环境信息 | [查看](./environment) |
|
| **logging** | 日志系统,记录运行日志 | [查看](./logging) |
|
||||||
| **localization** | 本地化系统,多语言支持 | [查看](./localization) |
|
| **environment** | 环境接口,提供运行环境信息 | [查看](./environment) |
|
||||||
|
| **localization** | 本地化系统,多语言支持 | [查看](./localization) |
|
||||||
|
|
||||||
## 组件联动
|
## 组件联动
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,10 @@ Property 包提供了可绑定属性(BindableProperty)的实现,支持属
|
|||||||
|
|
||||||
BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。
|
BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。
|
||||||
|
|
||||||
|
> 对于简单字段和局部 UI 绑定,`BindableProperty<T>` 仍然是首选方案。
|
||||||
|
> 如果你需要统一管理复杂状态树、通过 action / reducer 演进状态,或复用局部状态选择器,
|
||||||
|
> 请同时参考 [`state-management`](./state-management)。
|
||||||
|
|
||||||
## 核心接口
|
## 核心接口
|
||||||
|
|
||||||
### IReadonlyBindableProperty`<T>`
|
### IReadonlyBindableProperty`<T>`
|
||||||
@ -138,6 +142,43 @@ BindableProperty 基于事件系统实现属性变化通知:
|
|||||||
|
|
||||||
## 在 Model 中使用
|
## 在 Model 中使用
|
||||||
|
|
||||||
|
### 什么时候继续使用 BindableProperty
|
||||||
|
|
||||||
|
以下场景仍然优先推荐 `BindableProperty<T>`:
|
||||||
|
|
||||||
|
- 单个字段变化就能驱动视图更新
|
||||||
|
- 状态范围局限在单个 Model 内
|
||||||
|
- 不需要统一的 action / reducer 写入入口
|
||||||
|
- 不需要从聚合状态树中复用局部选择逻辑
|
||||||
|
|
||||||
|
如果你的状态已经演化为“多个字段必须一起更新”或“多个模块共享同一聚合状态”,
|
||||||
|
可以在 Model 内部组合 `Store<TState>`,而不是把所有字段都继续拆成独立属性。
|
||||||
|
|
||||||
|
### 与 Store / StateMachine 的边界
|
||||||
|
|
||||||
|
- `BindableProperty<T>`:字段级响应式值
|
||||||
|
- `Store<TState>`:聚合状态容器,负责统一归约状态变化
|
||||||
|
- `StateMachine`:流程状态切换,不负责数据状态归约
|
||||||
|
|
||||||
|
一个复杂 Model 可以同时持有 Store 和 BindableProperty:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PlayerStateModel : AbstractModel
|
||||||
|
{
|
||||||
|
public Store<PlayerState> Store { get; } = new(new PlayerState(100, "Player"));
|
||||||
|
public BindableProperty<bool> IsDirty { get; } = new(false);
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
Store.RegisterReducer<DamageAction>((state, action) =>
|
||||||
|
state with { Health = Math.Max(0, state.Health - action.Amount) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed record PlayerState(int Health, string Name);
|
||||||
|
public sealed record DamageAction(int Amount);
|
||||||
|
```
|
||||||
|
|
||||||
### 定义可绑定属性
|
### 定义可绑定属性
|
||||||
|
|
||||||
```csharp
|
```csharp
|
||||||
@ -422,11 +463,13 @@ _mOnValueChanged?.Invoke(value);
|
|||||||
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
|
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
|
||||||
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
|
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
|
||||||
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
|
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
|
||||||
|
7. **复杂聚合状态使用 Store** - 当多个字段必须统一演进时,使用 Store 管理聚合状态更清晰
|
||||||
|
|
||||||
## 相关包
|
## 相关包
|
||||||
|
|
||||||
- [`model`](./model.md) - Model 中大量使用 BindableProperty
|
- [`model`](./model.md) - Model 中大量使用 BindableProperty
|
||||||
- [`events`](./events.md) - BindableProperty 基于事件系统实现
|
- [`events`](./events.md) - BindableProperty 基于事件系统实现
|
||||||
|
- [`state-management`](./state-management) - 复杂状态树的集中式管理方案
|
||||||
- [`extensions`](./extensions.md) - 提供便捷的注销扩展方法
|
- [`extensions`](./extensions.md) - 提供便捷的注销扩展方法
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
153
docs/zh-CN/core/state-management.md
Normal file
153
docs/zh-CN/core/state-management.md
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
# State Management 包使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
State Management 提供一个可选的集中式状态容器方案,用于补足 `BindableProperty<T>` 在复杂状态树场景下的能力。
|
||||||
|
|
||||||
|
当你的状态具有以下特征时,推荐使用 `Store<TState>`:
|
||||||
|
|
||||||
|
- 多个字段需要在一次业务操作中协同更新
|
||||||
|
- 多个模块或 UI 片段共享同一聚合状态
|
||||||
|
- 希望所有状态写入都经过统一的 action / reducer 入口
|
||||||
|
- 需要对整棵状态树做局部选择和按片段订阅
|
||||||
|
|
||||||
|
这套能力不会替代现有 Property 机制,而是与其并存:
|
||||||
|
|
||||||
|
- `BindableProperty<T>`:字段级响应式值
|
||||||
|
- `Store<TState>`:聚合状态容器
|
||||||
|
- `StateMachine`:流程状态切换
|
||||||
|
|
||||||
|
## 核心接口
|
||||||
|
|
||||||
|
### IReadonlyStore`<TState>`
|
||||||
|
|
||||||
|
只读状态容器接口,提供:
|
||||||
|
|
||||||
|
- `State`:读取当前状态快照
|
||||||
|
- `Subscribe()`:订阅状态变化
|
||||||
|
- `SubscribeWithInitValue()`:订阅并立即回放当前状态
|
||||||
|
- `UnSubscribe()`:取消订阅
|
||||||
|
|
||||||
|
### IStore`<TState>`
|
||||||
|
|
||||||
|
在只读能力上增加:
|
||||||
|
|
||||||
|
- `Dispatch<TAction>()`:统一分发 action
|
||||||
|
|
||||||
|
### IReducer`<TState, TAction>`
|
||||||
|
|
||||||
|
定义状态归约逻辑:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public interface IReducer<TState, in TAction>
|
||||||
|
{
|
||||||
|
TState Reduce(TState currentState, TAction action);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### IStateSelector`<TState, TSelected>`
|
||||||
|
|
||||||
|
从整棵状态树中投影局部视图,便于 UI 和 Controller 复用选择逻辑。
|
||||||
|
|
||||||
|
## Store`<TState>`
|
||||||
|
|
||||||
|
`Store<TState>` 是默认实现,支持:
|
||||||
|
|
||||||
|
- 初始状态快照
|
||||||
|
- reducer 注册
|
||||||
|
- middleware 分发管线
|
||||||
|
- 只在状态真正变化时通知订阅者
|
||||||
|
- 基础诊断信息(最近一次 action、最近一次分发记录、最近一次状态变化时间)
|
||||||
|
|
||||||
|
## 基本示例
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.Core.StateManagement;
|
||||||
|
|
||||||
|
public sealed record PlayerState(int Health, string Name);
|
||||||
|
public sealed record DamageAction(int Amount);
|
||||||
|
public sealed record RenameAction(string Name);
|
||||||
|
|
||||||
|
var store = new Store<PlayerState>(new PlayerState(100, "Player"))
|
||||||
|
.RegisterReducer<DamageAction>((state, action) =>
|
||||||
|
state with { Health = Math.Max(0, state.Health - action.Amount) })
|
||||||
|
.RegisterReducer<RenameAction>((state, action) =>
|
||||||
|
state with { Name = action.Name });
|
||||||
|
|
||||||
|
store.SubscribeWithInitValue(state =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"{state.Name}: {state.Health}");
|
||||||
|
});
|
||||||
|
|
||||||
|
store.Dispatch(new DamageAction(25));
|
||||||
|
store.Dispatch(new RenameAction("Knight"));
|
||||||
|
```
|
||||||
|
|
||||||
|
## 选择器和 Bindable 风格桥接
|
||||||
|
|
||||||
|
Store 可以通过扩展方法把聚合状态投影成局部只读绑定视图:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using GFramework.Core.Extensions;
|
||||||
|
|
||||||
|
var healthSelection = store.Select(state => state.Health);
|
||||||
|
|
||||||
|
healthSelection.RegisterWithInitValue(health =>
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Current HP: {health}");
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
如果现有 UI 代码已经依赖 `IReadonlyBindableProperty<T>`,可以直接桥接:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
IReadonlyBindableProperty<int> healthProperty =
|
||||||
|
store.ToBindableProperty(state => state.Health);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 在 Model 中使用
|
||||||
|
|
||||||
|
推荐把 Store 作为 Model 的内部状态容器,由 Model 暴露领域友好的业务方法:
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
public class PlayerStateModel : AbstractModel
|
||||||
|
{
|
||||||
|
public Store<PlayerState> Store { get; } = new(new PlayerState(100, "Player"));
|
||||||
|
|
||||||
|
protected override void OnInit()
|
||||||
|
{
|
||||||
|
Store.RegisterReducer<DamageAction>((state, action) =>
|
||||||
|
state with { Health = Math.Max(0, state.Health - action.Amount) });
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeDamage(int amount)
|
||||||
|
{
|
||||||
|
Store.Dispatch(new DamageAction(amount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这样可以保留 Model 的生命周期和领域边界,同时获得统一状态入口。
|
||||||
|
|
||||||
|
## 什么时候不用 Store
|
||||||
|
|
||||||
|
以下情况继续优先使用 `BindableProperty<T>`:
|
||||||
|
|
||||||
|
- 单一字段直接绑定 UI
|
||||||
|
- 状态规模很小,不需要聚合归约
|
||||||
|
- 没有跨模块共享状态树的需求
|
||||||
|
- 你只需要“值变化通知”,不需要“统一状态演进入口”
|
||||||
|
|
||||||
|
## 最佳实践
|
||||||
|
|
||||||
|
1. 优先把 `TState` 设计为不可变状态(如 `record`)
|
||||||
|
2. 让 reducer 保持纯函数风格,不在 reducer 内执行副作用
|
||||||
|
3. 使用 selector 暴露局部状态,而不是让 UI 自己解析整棵状态树
|
||||||
|
4. 需要日志或诊断时,优先通过 middleware 扩展,而不是把横切逻辑塞进 reducer
|
||||||
|
|
||||||
|
## 相关文档
|
||||||
|
|
||||||
|
- [`property`](./property) - 字段级响应式属性
|
||||||
|
- [`model`](./model) - Store 常见承载位置
|
||||||
|
- [`events`](./events) - 组件间事件通信
|
||||||
|
- [`state-machine-tutorial`](../tutorials/state-machine-tutorial) - 流程状态切换能力
|
||||||
Loading…
x
Reference in New Issue
Block a user