# 值对象
**本文引用的文件**
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs)
- [IBindableProperty.cs](file://GFramework.Core.Abstractions/property/IBindableProperty.cs)
- [BindablePropertyUnRegister.cs](file://GFramework.Core/property/BindablePropertyUnRegister.cs)
- [README.md(Property 包)](file://GFramework.Core/property/README.md)
- [AbstractModel.cs](file://GFramework.Core/model/AbstractModel.cs)
- [README.md(GFramework.Core)](file://GFramework.Core/README.md)
- [advanced-patterns.md](file://docs/tutorials/advanced-patterns.md)
- [AudioSettings.cs](file://GFramework.Game.Abstractions/setting/AudioSettings.cs)
- [GraphicsSettings.cs](file://GFramework.Game.Abstractions/setting/GraphicsSettings.cs)
- [ArchitectureProperties.cs](file://GFramework.Core.Abstractions/properties/ArchitectureProperties.cs)
- [IsExternalInit.cs](file://GFramework.Game.Abstractions/internals/IsExternalInit.cs)
- [BindablePropertyTests.cs](file://GFramework.Core.Tests/property/BindablePropertyTests.cs)
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 引言
本篇文档聚焦于 GFramework 中“值对象”的概念、设计原则与实现要点,结合框架内的可绑定属性(BindableProperty)与领域驱动设计(DDD)示例,系统阐述值对象的不可变性、相等性比较、构造函数验证、线程安全与组合嵌套等关键主题,并给出在游戏开发中的典型应用场景(位置坐标、颜色值、货币金额、技能等级等),以及与 GFramework 属性系统的集成方式。
## 项目结构
围绕值对象与属性系统的相关文件主要分布在以下模块:
- Core 层属性系统:可绑定属性类及其接口、注销器、使用说明与测试
- 抽象层接口:定义属性契约
- 示例与文档:DDD 值对象示例、设置类示例、架构配置示例
- 测试:可绑定属性的行为验证
```mermaid
graph TB
subgraph "Core 属性系统"
BP["BindableProperty"]
IBind["IBindableProperty"]
UnReg["BindablePropertyUnRegister"]
PropReadme["Property 包使用说明"]
end
subgraph "抽象层接口"
AbsProp["IBindableProperty 接口"]
ArchProps["ArchitectureProperties"]
end
subgraph "示例与文档"
DDD["advanced-patterns.md
值对象示例"]
Audio["AudioSettings.cs"]
Graphics["GraphicsSettings.cs"]
ExtInit["IsExternalInit.cs"]
end
subgraph "测试"
BPT["BindablePropertyTests.cs"]
end
BP --> IBind
UnReg --> BP
PropReadme --> BP
AbsProp --> IBind
ArchProps --> PropReadme
DDD --> BP
Audio --> DDD
Graphics --> DDD
BPT --> BP
```
**图表来源**
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs#L1-L135)
- [IBindableProperty.cs](file://GFramework.Core.Abstractions/property/IBindableProperty.cs#L1-L19)
- [BindablePropertyUnRegister.cs](file://GFramework.Core/property/BindablePropertyUnRegister.cs#L1-L39)
- [README.md(Property 包)](file://GFramework.Core/property/README.md#L1-L342)
- [ArchitectureProperties.cs](file://GFramework.Core.Abstractions/properties/ArchitectureProperties.cs#L1-L17)
- [advanced-patterns.md](file://docs/tutorials/advanced-patterns.md#L208-L513)
- [AudioSettings.cs](file://GFramework.Game.Abstractions/setting/AudioSettings.cs#L1-L32)
- [GraphicsSettings.cs](file://GFramework.Game.Abstractions/setting/GraphicsSettings.cs#L1-L32)
- [IsExternalInit.cs](file://GFramework.Game.Abstractions/internals/IsExternalInit.cs#L1-L11)
- [BindablePropertyTests.cs](file://GFramework.Core.Tests/property/BindablePropertyTests.cs#L1-L186)
**章节来源**
- [README.md(GFramework.Core)](file://GFramework.Core/README.md#L1-L508)
## 核心组件
- 可绑定属性(BindableProperty)
- 提供值变化监听与事件通知,支持自定义比较器、无事件设置、注册/注销等能力
- 通过静态比较器与实例比较器实现相等性判断,避免无效触发
- 可绑定属性接口(IBindableProperty)
- 定义可读写属性值与无事件设置方法
- 注销器(BindablePropertyUnRegister)
- 负责取消监听并清理引用,确保生命周期内资源释放
- 值对象示例(来自 DDD 文档)
- 使用 record 定义不可变值对象,构造函数中进行参数校验,提供派生构造与安全更新方法
- 设置类(示例)
- 音频设置、图形设置等作为普通类值对象,提供默认值与 Reset 行为
**章节来源**
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs#L1-L135)
- [IBindableProperty.cs](file://GFramework.Core.Abstractions/property/IBindableProperty.cs#L1-L19)
- [BindablePropertyUnRegister.cs](file://GFramework.Core/property/BindablePropertyUnRegister.cs#L1-L39)
- [README.md(Property 包)](file://GFramework.Core/property/README.md#L1-L342)
- [advanced-patterns.md](file://docs/tutorials/advanced-patterns.md#L208-L513)
- [AudioSettings.cs](file://GFramework.Game.Abstractions/setting/AudioSettings.cs#L1-L32)
- [GraphicsSettings.cs](file://GFramework.Game.Abstractions/setting/GraphicsSettings.cs#L1-L32)
## 架构总览
值对象与属性系统在 GFramework 中的协作关系如下:
- Model 层通过 BindableProperty 暴露可监听的状态
- Controller/View 通过注册监听实现数据绑定与 UI 更新
- 事件系统与属性系统共同构成响应式数据流
```mermaid
sequenceDiagram
participant Model as "Model可绑定属性"
participant Controller as "ControllerUI/逻辑"
participant Bus as "事件系统"
participant View as "ViewUI"
Controller->>Model : 注册监听Register/ RegisterWithInitValue
Model-->>Controller : 初始值回传RegisterWithInitValue
Controller->>Model : 设置值Value = ...
Model->>Model : 比较器判断相等性
alt 值变化
Model-->>Controller : 触发回调
Controller->>View : 更新 UI
Controller->>Bus : 发送事件可选
else 值未变化
Model-->>Controller : 不触发回调
end
```
**图表来源**
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs#L24-L41)
- [README.md(Property 包)](file://GFramework.Core/property/README.md#L131-L171)
- [README.md(GFramework.Core)](file://GFramework.Core/README.md#L339-L346)
## 详细组件分析
### 组件一:可绑定属性(BindableProperty)
- 不可变性与构造函数验证
- 通过构造函数设定默认值;在设置值前进行相等性判断,避免无效触发
- 支持静态比较器与 WithComparer 实例方法,满足不同场景下的相等性语义
- 相等性比较
- 默认使用 EqualityComparer.Default 进行比较;可通过 Comparer 静态字段或 WithComparer 自定义
- 针对浮点数等场景,建议使用 WithComparer 提供容差比较,减少抖动触发
- 线程安全
- 属性值的读写与事件触发在单线程上下文中进行;若需跨线程访问,应在调用方保证线程安全
- 事件与注销
- Register/RegisterWithInitValue 提供监听注册;UnRegister 解除监听;BindablePropertyUnRegister 负责清理
- 与 Model 的集成
- Model 层定义 BindableProperty,Controller 层注册监听实现 UI 绑定;测试覆盖了基本行为与自定义比较器
```mermaid
classDiagram
class BindableProperty_T_ {
+T Value
+static Comparer(a,b) bool
+Register(handler) IUnRegister
+RegisterWithInitValue(action) IUnRegister
+UnRegister(handler) void
+SetValueWithoutEvent(newValue) void
+WithComparer(comparer) BindableProperty_T_
#SetValue(newValue) void
#GetValue() T
+ToString() string
}
class IBindableProperty_T_ {
+T Value
+SetValueWithoutEvent(newValue) void
}
class BindablePropertyUnRegister_T_ {
+BindableProperty BindableProperty
+Action OnValueChanged
+UnRegister() void
}
BindableProperty_T_ ..|> IBindableProperty_T_
BindablePropertyUnRegister_T_ --> BindableProperty_T_ : "注销监听"
```
**图表来源**
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs#L11-L135)
- [IBindableProperty.cs](file://GFramework.Core.Abstractions/property/IBindableProperty.cs#L7-L19)
- [BindablePropertyUnRegister.cs](file://GFramework.Core/property/BindablePropertyUnRegister.cs#L11-L39)
**章节来源**
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs#L11-L135)
- [IBindableProperty.cs](file://GFramework.Core.Abstractions/property/IBindableProperty.cs#L1-L19)
- [BindablePropertyUnRegister.cs](file://GFramework.Core/property/BindablePropertyUnRegister.cs#L1-L39)
- [README.md(Property 包)](file://GFramework.Core/property/README.md#L1-L342)
- [BindablePropertyTests.cs](file://GFramework.Core.Tests/property/BindablePropertyTests.cs#L1-L186)
### 组件二:值对象(DDD 示例)
- 不可变性
- 使用 record 定义值对象,字段为只读,构造后不可变
- 构造函数验证
- 在构造函数中进行参数校验(如范围、非空、格式),不符合条件抛出异常
- 安全更新
- 通过返回新实例的方式实现“更新”,保持原值不变
- 组合与嵌套
- 值对象可作为其他值对象的组成部分(如 Health 同时包含当前值与最大值)
```mermaid
flowchart TD
Start(["创建值对象"]) --> Validate["构造函数参数校验"]
Validate --> Valid{"校验通过?"}
Valid --> |否| Throw["抛出异常"]
Valid --> |是| Build["构建不可变实例"]
Build --> Use["在领域逻辑中使用"]
Use --> Update["返回新实例组合/嵌套"]
Update --> Use
```
**图表来源**
- [advanced-patterns.md](file://docs/tutorials/advanced-patterns.md#L371-L435)
**章节来源**
- [advanced-patterns.md](file://docs/tutorials/advanced-patterns.md#L208-L513)
### 组件三:设置类(值对象示例)
- 音频设置(AudioSettings)
- 包含主音量、背景音乐音量、音效音量等属性,提供 Reset 方法恢复默认值
- 图形设置(GraphicsSettings)
- 包含全屏、分辨率宽高属性,提供 Reset 方法恢复默认值
- 适用场景
- 作为持久化配置的值对象载体,便于序列化与版本迁移
**章节来源**
- [AudioSettings.cs](file://GFramework.Game.Abstractions/setting/AudioSettings.cs#L1-L32)
- [GraphicsSettings.cs](file://GFramework.Game.Abstractions/setting/GraphicsSettings.cs#L1-L32)
### 组件四:架构属性(ArchitectureProperties)
- AllowLateRegistration:允许在初始化完成后进行组件注册
- StrictPhaseValidation:启用严格的阶段验证机制
- 与值对象的关系
- 架构配置可影响值对象所在组件的生命周期与注册时机,间接影响值对象的可用性
**章节来源**
- [ArchitectureProperties.cs](file://GFramework.Core.Abstractions/properties/ArchitectureProperties.cs#L1-L17)
## 依赖关系分析
- BindableProperty 依赖 IBindableProperty 接口与事件系统(通过注册回调实现通知)
- Model 通过 AbstractModel 提供生命周期钩子,便于在 OnInit 中初始化可绑定属性
- 值对象示例与设置类作为数据载体,与属性系统协同工作
- 测试用例覆盖了基本行为、比较器与注销流程
```mermaid
graph LR
IBind["IBindableProperty"] --> BP["BindableProperty"]
BP --> UnReg["BindablePropertyUnRegister"]
Model["AbstractModel"] --> BP
DDD["值对象示例"] --> BP
Settings["设置类示例"] --> BP
Tests["BindablePropertyTests"] --> BP
```
**图表来源**
- [IBindableProperty.cs](file://GFramework.Core.Abstractions/property/IBindableProperty.cs#L1-L19)
- [BindableProperty.cs](file://GFramework.Core/property/BindableProperty.cs#L1-L135)
- [BindablePropertyUnRegister.cs](file://GFramework.Core/property/BindablePropertyUnRegister.cs#L1-L39)
- [AbstractModel.cs](file://GFramework.Core/model/AbstractModel.cs#L1-L34)
- [advanced-patterns.md](file://docs/tutorials/advanced-patterns.md#L208-L513)
- [AudioSettings.cs](file://GFramework.Game.Abstractions/setting/AudioSettings.cs#L1-L32)
- [GraphicsSettings.cs](file://GFramework.Game.Abstractions/setting/GraphicsSettings.cs#L1-L32)
- [BindablePropertyTests.cs](file://GFramework.Core.Tests/property/BindablePropertyTests.cs#L1-L186)
**章节来源**
- [README.md(GFramework.Core)](file://GFramework.Core/README.md#L1-L508)
## 性能考量
- 避免频繁触发
- 使用 SetValueWithoutEvent 进行批量更新,最后统一触发一次事件,降低 UI 刷新与事件风暴
- 自定义比较器
- 对浮点数等需要精度控制的属性,使用 WithComparer 提供容差比较,减少因微小差异导致的无效触发
- 监听管理
- 使用 RegisterWithInitValue 首次即获取当前值,避免重复渲染;及时注销监听,防止内存泄漏与逻辑错误
**章节来源**
- [README.md(Property 包)](file://GFramework.Core/property/README.md#L278-L302)
- [README.md(GFramework.Core)](file://GFramework.Core/README.md#L403-L429)
## 故障排查指南
- 设置相同值仍触发事件
- 检查 Comparer 是否被全局或实例级自定义覆盖,确认比较逻辑是否符合预期
- 注销后仍收到回调
- 确认是否正确调用 UnRegister 或使用 BindablePropertyUnRegister 的 UnRegister 方法
- UI 不更新
- 确认使用 RegisterWithInitValue 获取初始值;检查监听是否在正确的生命周期内注册
- 测试失败
- 参考单元测试用例,验证默认行为、比较器、注销与 ToString 等关键路径
**章节来源**
- [BindablePropertyTests.cs](file://GFramework.Core.Tests/property/BindablePropertyTests.cs#L1-L186)
- [README.md(Property 包)](file://GFramework.Core/property/README.md#L1-L342)
## 结论
值对象在 GFramework 中通过 record 的不可变性与构造函数验证实现强健的数据建模;与可绑定属性系统结合,既保证了数据一致性,又实现了响应式的数据绑定与事件通知。通过自定义比较器、批量更新与完善的监听管理,能够在游戏开发中高效地处理位置坐标、颜色值、货币金额、技能等级等常见场景,并与架构配置、事件系统形成一致的开发范式。
## 附录
- 术语
- 值对象:仅由数据组成、不可变、按值比较的领域对象
- 可绑定属性:支持监听与事件通知的属性封装
- 参考路径
- [Property 包使用说明](file://GFramework.Core/property/README.md#L1-L342)
- [GFramework.Core 概览](file://GFramework.Core/README.md#L1-L508)
- [DDD 值对象示例](file://docs/tutorials/advanced-patterns.md#L208-L513)
- [IsExternalInit(兼容 init-only setter)](file://GFramework.Game.Abstractions/internals/IsExternalInit.cs#L1-L11)