mirror of
https://github.com/motajs/template.git
synced 2026-10-19 21:30:30 +08:00
fix(07-11): #06-17-5 preserve flag field instances across loadState
- FlagSystem.loadState 按 key 复用现有 FlagCommonField 并用 fromStructured 原地写值 - 存档中不存在的字段一律删除(以存档为准) - IFlagSystem.loadState jsDoc 补写同引用与删除语义 - 新增同实例保真、getOrInsert 复用、结构化值恢复、删除多余字段回归用例 - flag/saveLoad.test.ts 的「重建字段对象」断言按 Q1=A 裁决改为同实例保真
This commit is contained in:
parent
153f4a490a
commit
9964326210
@ -64,15 +64,15 @@ describe('FlagSystem save and load round trips', () => {
|
||||
expect(system.occupied('extra')).toBe(false);
|
||||
});
|
||||
|
||||
// 验证 loadState 会重建字段对象并保留恢复值
|
||||
it('rebuilds field objects on loadState', () => {
|
||||
// 验证 loadState 在同实例上原地恢复字段值(#06-17-5)
|
||||
it('keeps field objects on loadState', () => {
|
||||
const system = new FlagSystem();
|
||||
const before = system.insertField('score', 7);
|
||||
|
||||
const saved = system.saveState();
|
||||
system.loadState(saved);
|
||||
|
||||
expect(system.getField('score')).not.toBe(before);
|
||||
expect(system.getField('score')).toBe(before);
|
||||
expect(system.getFieldValue<number>('score')).toBe(7);
|
||||
});
|
||||
});
|
||||
|
||||
@ -169,3 +169,61 @@ describe('FlagSystem value accessors', () => {
|
||||
expect(system.getFieldValue<number>('lives')).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('FlagSystem same-reference load (#06-17-5)', () => {
|
||||
// 验证同 key 字段跨读档为同一实例,且数值恢复到存档点
|
||||
it('keeps the same field instance and restores its value', () => {
|
||||
const system = new modules.FlagSystem();
|
||||
const before = system.getOrInsert('score', 5);
|
||||
before.set(7);
|
||||
|
||||
const saved = system.saveState();
|
||||
before.set(99);
|
||||
|
||||
system.loadState(saved);
|
||||
|
||||
expect(system.getField('score')).toBe(before);
|
||||
expect(before.get()).toBe(7);
|
||||
});
|
||||
|
||||
// 验证读档覆盖后 getOrInsert 仍返回此前持有的同一实例
|
||||
it('reuses the held field instance through getOrInsert after load', () => {
|
||||
const system = new modules.FlagSystem();
|
||||
const before = system.getOrInsert('count', 1);
|
||||
|
||||
const saved = system.saveState();
|
||||
before.set(99);
|
||||
system.loadState(saved);
|
||||
|
||||
expect(system.getOrInsert('count', 42)).toBe(before);
|
||||
expect(before.get()).toBe(1);
|
||||
});
|
||||
|
||||
// 验证复杂字段值在原地复用实例上以存档克隆恢复
|
||||
it('restores structured field values on the same instance', () => {
|
||||
const system = new modules.FlagSystem();
|
||||
const before = system.getOrInsert('nested', { list: [1, 2] });
|
||||
|
||||
const saved = system.saveState();
|
||||
before.set({ list: [9] });
|
||||
system.loadState(saved);
|
||||
|
||||
expect(system.getField('nested')).toBe(before);
|
||||
expect(before.get()).toEqual({ list: [1, 2] });
|
||||
});
|
||||
|
||||
// 验证存档中不存在的字段在读档后被删除(以存档为准)
|
||||
it('deletes fields absent from the save', () => {
|
||||
const system = new modules.FlagSystem();
|
||||
system.setFieldValue('score', 7);
|
||||
const kept = system.getField('score');
|
||||
|
||||
const saved = system.saveState();
|
||||
system.setFieldValue('extra', 1);
|
||||
|
||||
system.loadState(saved);
|
||||
|
||||
expect(system.getField('score')).toBe(kept);
|
||||
expect(system.occupied('extra')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -63,11 +63,26 @@ export class FlagSystem implements IFlagSystem {
|
||||
}
|
||||
|
||||
loadState(state: IFlagSystemSave): void {
|
||||
this.fieldMap.clear();
|
||||
// 按 key 复用现有字段原地写值,使外部持有的引用跨读档仍然有效
|
||||
for (const [key, data] of state.fields) {
|
||||
const field = new FlagCommonField<unknown>(this, key, undefined);
|
||||
field.fromStructured(data);
|
||||
this.fieldMap.set(key, field);
|
||||
const existing = this.fieldMap.get(key);
|
||||
if (existing) {
|
||||
existing.fromStructured(data);
|
||||
} else {
|
||||
const field = new FlagCommonField<unknown>(
|
||||
this,
|
||||
key,
|
||||
undefined
|
||||
);
|
||||
field.fromStructured(data);
|
||||
this.fieldMap.set(key, field);
|
||||
}
|
||||
}
|
||||
// 以存档为准:存档中不存在的字段一律删除
|
||||
for (const key of this.fieldMap.keys()) {
|
||||
if (!state.fields.has(key)) {
|
||||
this.fieldMap.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
//#region 字段
|
||||
|
||||
import { ISaveableContent } from '@user/data-common';
|
||||
import { ISaveableContent, SaveCompression } from '@user/data-common';
|
||||
|
||||
export interface IFlagCommonField<T> {
|
||||
/** 此字段所处的 Flag 系统 */
|
||||
@ -133,6 +133,15 @@ export interface IFlagSystem extends ISaveableContent<IFlagSystemSave> {
|
||||
* @param defaultValue 字段默认值
|
||||
*/
|
||||
getFieldValueDefaults<T>(field: PropertyKey, defaultValue: T): T;
|
||||
|
||||
/**
|
||||
* 读取 Flag 系统存档。存档中已存在的字段会复用原实例并在其实例上原地写值,
|
||||
* 因此外部通过 {@link getField} / {@link getOrInsert} 持有的字段引用跨读档仍然有效;
|
||||
* 存档中不存在的字段一律删除,即读档结果以存档为准。
|
||||
* @param state Flag 系统存档
|
||||
* @param compression 压缩级别
|
||||
*/
|
||||
loadState(state: IFlagSystemSave, compression: SaveCompression): void;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
Loading…
Reference in New Issue
Block a user