mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-24 04:06:48 +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)
|
||||
- 依赖注入容器接口 (IIocContainer)
|
||||
- 可绑定属性接口 (IBindableProperty)
|
||||
- 状态管理接口 (IStore, IReducer, IStateSelector)
|
||||
- 日志系统接口 (ILogger)
|
||||
|
||||
## 设计原则
|
||||
|
||||
@ -13,6 +13,7 @@ GFramework 框架的核心模块,提供MVC架构的基础设施。
|
||||
- **Events** - 事件系统,实现组件间松耦合通信
|
||||
- **IoC** - 轻量级依赖注入容器
|
||||
- **Property** - 可绑定属性,支持数据绑定和响应式编程
|
||||
- **StateManagement** - 集中式状态容器,支持状态归约、选择器和诊断
|
||||
- **Utility** - 无状态工具类
|
||||
- **Pool** - 对象池系统,减少GC压力
|
||||
- **Extensions** - 框架扩展方法
|
||||
|
||||
@ -105,7 +105,7 @@ Any → FailedInitialization
|
||||
- 初始化/销毁 - Utility 注册
|
||||
```
|
||||
|
||||
这种设计遵循单一职责原则,使代码更易维护和测试。
|
||||
这种设计遵循单一职责原则,使代码更易维护和测试。
|
||||
|
||||
```
|
||||
┌──────────────────┐
|
||||
@ -398,30 +398,31 @@ public class PlayerController : IController
|
||||
4. **易于扩展**: 添加新功能更容易
|
||||
5. **代码安全**: 消除了 `null!` 断言,所有字段在构造后立即可用
|
||||
|
||||
详细的设计决策已在架构实现重构中落地。
|
||||
详细的设计决策已在架构实现重构中落地。
|
||||
|
||||
---
|
||||
|
||||
## 包说明
|
||||
|
||||
| 包名 | 职责 | 文档 |
|
||||
|------------------|-----------------|----------------------|
|
||||
| **architecture** | 架构核心,管理所有组件生命周期 | [查看](./architecture) |
|
||||
| **constants** | 框架常量定义 | 本文档 |
|
||||
| **model** | 数据模型层,存储状态 | [查看](./model) |
|
||||
| **system** | 业务逻辑层,处理业务规则 | [查看](./system) |
|
||||
| **controller** | 控制器层,连接视图和逻辑 | (在 Abstractions 中) |
|
||||
| **utility** | 工具类层,提供无状态工具 | [查看](./utility) |
|
||||
| **command** | 命令模式,封装写操作 | [查看](./command) |
|
||||
| **query** | 查询模式,封装读操作 | [查看](./query) |
|
||||
| **events** | 事件系统,组件间通信 | [查看](./events) |
|
||||
| **property** | 可绑定属性,响应式编程 | [查看](./property) |
|
||||
| **ioc** | IoC 容器,依赖注入 | [查看](./ioc) |
|
||||
| **rule** | 规则接口,定义组件约束 | [查看](./rule) |
|
||||
| **extensions** | 扩展方法,简化 API 调用 | [查看](./extensions) |
|
||||
| **logging** | 日志系统,记录运行日志 | [查看](./logging) |
|
||||
| **environment** | 环境接口,提供运行环境信息 | [查看](./environment) |
|
||||
| **localization** | 本地化系统,多语言支持 | [查看](./localization) |
|
||||
| 包名 | 职责 | 文档 |
|
||||
|----------------------|-----------------|--------------------------|
|
||||
| **architecture** | 架构核心,管理所有组件生命周期 | [查看](./architecture) |
|
||||
| **constants** | 框架常量定义 | 本文档 |
|
||||
| **model** | 数据模型层,存储状态 | [查看](./model) |
|
||||
| **system** | 业务逻辑层,处理业务规则 | [查看](./system) |
|
||||
| **controller** | 控制器层,连接视图和逻辑 | (在 Abstractions 中) |
|
||||
| **utility** | 工具类层,提供无状态工具 | [查看](./utility) |
|
||||
| **command** | 命令模式,封装写操作 | [查看](./command) |
|
||||
| **query** | 查询模式,封装读操作 | [查看](./query) |
|
||||
| **events** | 事件系统,组件间通信 | [查看](./events) |
|
||||
| **property** | 可绑定属性,响应式编程 | [查看](./property) |
|
||||
| **state-management** | 集中式状态容器与选择器 | [查看](./state-management) |
|
||||
| **ioc** | IoC 容器,依赖注入 | [查看](./ioc) |
|
||||
| **rule** | 规则接口,定义组件约束 | [查看](./rule) |
|
||||
| **extensions** | 扩展方法,简化 API 调用 | [查看](./extensions) |
|
||||
| **logging** | 日志系统,记录运行日志 | [查看](./logging) |
|
||||
| **environment** | 环境接口,提供运行环境信息 | [查看](./environment) |
|
||||
| **localization** | 本地化系统,多语言支持 | [查看](./localization) |
|
||||
|
||||
## 组件联动
|
||||
|
||||
|
||||
@ -2,9 +2,13 @@
|
||||
|
||||
## 概述
|
||||
|
||||
Property 包提供了可绑定属性(BindableProperty)的实现,支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。
|
||||
|
||||
BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。
|
||||
Property 包提供了可绑定属性(BindableProperty)的实现,支持属性值的监听和响应式编程。这是实现数据绑定和响应式编程的核心组件。
|
||||
|
||||
BindableProperty 是 GFramework 中 Model 层数据管理的基础,通过事件机制实现属性变化的通知。
|
||||
|
||||
> 对于简单字段和局部 UI 绑定,`BindableProperty<T>` 仍然是首选方案。
|
||||
> 如果你需要统一管理复杂状态树、通过 action / reducer 演进状态,或复用局部状态选择器,
|
||||
> 请同时参考 [`state-management`](./state-management)。
|
||||
|
||||
## 核心接口
|
||||
|
||||
@ -136,7 +140,44 @@ BindableProperty 基于事件系统实现属性变化通知:
|
||||
3. **事件触发**:如果值发生变化,调用所有注册的回调函数
|
||||
4. **内存管理**:通过 `IUnRegister` 机制管理监听器的生命周期
|
||||
|
||||
## 在 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);
|
||||
```
|
||||
|
||||
### 定义可绑定属性
|
||||
|
||||
@ -416,18 +457,20 @@ _mOnValueChanged?.Invoke(value);
|
||||
|
||||
## 最佳实践
|
||||
|
||||
1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层
|
||||
2. **使用只读接口暴露** - 防止外部随意修改
|
||||
3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree
|
||||
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
|
||||
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
|
||||
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
|
||||
1. **在 Model 中定义属性** - BindableProperty 主要用于 Model 层
|
||||
2. **使用只读接口暴露** - 防止外部随意修改
|
||||
3. **及时注销监听** - 使用 UnRegisterList 或 UnRegisterWhenNodeExitTree
|
||||
4. **使用 RegisterWithInitValue** - UI 绑定时立即获取初始值
|
||||
5. **避免循环依赖** - 属性监听器中修改其他属性要小心
|
||||
6. **使用自定义比较器** - 对于浮点数等需要精度控制的属性
|
||||
7. **复杂聚合状态使用 Store** - 当多个字段必须统一演进时,使用 Store 管理聚合状态更清晰
|
||||
|
||||
## 相关包
|
||||
|
||||
- [`model`](./model.md) - Model 中大量使用 BindableProperty
|
||||
- [`events`](./events.md) - BindableProperty 基于事件系统实现
|
||||
- [`extensions`](./extensions.md) - 提供便捷的注销扩展方法
|
||||
- [`model`](./model.md) - Model 中大量使用 BindableProperty
|
||||
- [`events`](./events.md) - BindableProperty 基于事件系统实现
|
||||
- [`state-management`](./state-management) - 复杂状态树的集中式管理方案
|
||||
- [`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