# 弟子管理模块设计 模块位置:P1 核心循环(见 `doc/Roadmap.md`)。本文档描述弟子数据模型与弟子管理器的设计。 ## 1. 结构:数据类 + 管理器 弟子模块拆分为两层: | 层 | 文件 | 类型 | 职责 | | --- | --- | --- | --- | | 弟子数据类 | `src/Data/Models/Disciple.gd` | RefCounted | 持有弟子的所有信息,纯数据,无逻辑 | | 弟子管理器 | `src/Character/DiscipleManager.gd` | Node | 添加、删除、查询、修改弟子属性 + 领域操作(修炼结算) | ## 2. 设计模式 | 角色 | 模式 | 说明 | | --- | --- | --- | | 弟子数据类 | 纯数据模型 | RefCounted,运行时可变数据,不依赖场景树,可脱离场景单测 | | 弟子管理器 | 仓储模式(Repository) | CRUD + 查询的集中入口,是弟子数据的唯一管理者 | | 变化通知 | 观察者模式(Observer) | 管理器发信号,UI/其他系统订阅实时刷新 | | 弟子创建 | 工厂方法(Factory) | 初始弟子/收徒的创建逻辑收在管理器内 | **明确不用的方案**: - 不用 ECS:弟子属性固定(境界/技艺/忠诚…),无动态组件需求,是过度设计。 - 不用 Resource/.tres:弟子是运行时可变数据,RefCounted 类最轻量,不走序列化资源。 - 不把所有逻辑塞进数据类:领域操作归管理器,保证修改只有单一入口,信号好通知,测试好做。 ## 3. Disciple 数据类(RefCounted,纯数据) ```gdscript var name: String var is_mortal: bool # 凡人弟子 var realm_level: int = 1 # 境界 1-9 var cultivation_skill: int = 1 # 种植技艺 1-10 var alchemy_skill: int = 1 # 炼丹技艺 1-10 var loyalty: int = 50 # 忠诚 0-100(P1 仅存字段) var cultivation_exp: int = 0 # 修为进度 ``` 只读派生(不存储,由属性计算): - `monthly_cost() -> int`:每月修炼灵石消耗(凡人弟子为 0) - `realm_name() -> String`:境界显示名(凡人 / 炼气X层…) - `exp_to_next() -> int`:升到下一境界所需修为 ## 4. DiscipleManager 管理器(Node,仓储) ```gdscript signal disciple_added(disciple) signal disciple_removed(disciple) signal disciple_attr_changed(disciple, attr_name, old_value, new_value) func add_disciple(d) -> void func remove_disciple(d) -> void func get_disciple(id) -> Disciple func get_all() -> Array[Disciple] func get_by_realm(level) -> Array[Disciple] func count_mortals() -> int func set_attr(d, attr, value) -> void # 修改唯一入口,内部发信号 func settle_month() -> void # 修炼结算(领域操作) func to_dict() -> Dictionary # 存档网关 func load_from_dict(d) -> void ``` ## 5. 关键设计决策 1. **修改只走管理器**——`set_attr()` 是唯一变更入口,内部统一发 `disciple_attr_changed`。UI 订阅它实时刷新,也为以后忠诚度/事件系统留监听点。 2. **查询返回引用**——性能好(P1 规模无碍),但禁止外部直接写属性;要写必须走管理器。 3. **管理器是 Node,数据类是 RefCounted**——管理器可挂 MainGame/测试场景;数据类脱离场景树也能单测。 4. **领域操作在管理器**——`settle_month()`(修炼消耗灵石、增加修为、境界提升)归管理器,数据类不碰业务逻辑。 5. **存档网关(Unit of Work)**——序列化/恢复只经过管理器 `to_dict()/load_from_dict()`,为 SaveSystem 注册预留。 ## 6. 测试方式 独立测试场景 `src/Character/test_disciple.tscn`: 1. 实例化 DiscipleManager(不依赖其他模块) 2. `_ready()` 中自动执行用例: - 添加/删除弟子 → 信号是否正确发出 - 查询(get_by_realm / count_mortals) - `set_attr` → 信号带旧值/新值 - `settle_month` → 境界提升/灵石消耗正确 - `to_dict / load_from_dict` 往返一致 3. `print` 输出 PASS/FAIL,编辑器直接运行场景验证