refactor(architecture): 重构架构初始化和销毁机制

- 将Init方法统一重命名为Initialize方法以提高一致性
- 修改Architecture类中的组件注册逻辑,优化去重判断
- 更新ECS系统基础类以使用新的初始化接口
- 重构EcsWorld类使用属性自动实现而非私有字段
- 移除过时的EcsUsageExample示例文件
- 更新相关测试类以匹配新的初始化方法命名
- 改进代码注释和文档字符串格式
This commit is contained in:
GeWuYou 2026-02-23 10:54:35 +08:00 committed by gewuyou
parent 35845be93f
commit 3db89ab498
33 changed files with 112 additions and 180 deletions

View File

@ -10,7 +10,7 @@ namespace GFramework.Core.Abstractions.architecture;
/// 架构接口,专注于生命周期管理,包括系统、模型、工具的注册和获取
/// 业务操作通过 ArchitectureRuntime 提供
/// </summary>
public interface IArchitecture : IAsyncInitializable
public interface IArchitecture : IAsyncInitializable, IAsyncDestroyable, IInitializable, IDestroyable
{
/// <summary>
/// 获取架构上下文
@ -25,22 +25,6 @@ public interface IArchitecture : IAsyncInitializable
/// </value>
Action<IServiceCollection>? Configurator { get; }
/// <summary>
/// 初始化方法,用于执行对象的初始化操作
/// </summary>
/// <remarks>
/// 该方法通常用于设置初始状态、初始化成员变量或执行必要的预处理操作
/// </remarks>
void Initialize();
/// <summary>
/// 销毁方法,用于执行对象的清理和销毁操作
/// </summary>
/// <remarks>
/// 该方法通常用于释放资源、清理内存或执行必要的清理操作
/// </remarks>
void Destroy();
/// <summary>
/// 注册系统实例到架构中
/// </summary>

View File

@ -8,5 +8,5 @@ public interface IInitializable
/// <summary>
/// 初始化组件
/// </summary>
void Init();
void Initialize();
}

View File

@ -321,7 +321,7 @@ public class TestSystemV2 : ISystem
return _context;
}
public void Init()
public void Initialize()
{
}
@ -349,7 +349,7 @@ public class TestModelV2 : IModel
return _context;
}
public void Init()
public void Initialize()
{
}

View File

@ -11,12 +11,12 @@ public class AsyncTestArchitecture : TestArchitectureBase
/// <summary>
/// 异步初始化架构
/// </summary>
protected override void Init()
protected override void OnInitialize()
{
// 注册模型
RegisterModel(new AsyncTestModel());
// 注册系统
RegisterSystem(new AsyncTestSystem());
base.Init();
base.OnInitialize();
}
}

View File

@ -222,7 +222,7 @@ public class TestArchitecture : Architecture
/// <summary>
/// 初始化方法,当前为空实现
/// </summary>
protected override void Init()
protected override void OnInitialize()
{
}
}

View File

@ -11,10 +11,10 @@ public sealed class SyncTestArchitecture : TestArchitectureBase
/// <summary>
/// 初始化架构组件,注册模型、系统并设置事件监听器
/// </summary>
protected override void Init()
protected override void OnInitialize()
{
RegisterModel(new TestModel());
RegisterSystem(new TestSystem());
base.Init();
base.OnInitialize();
}
}

View File

@ -37,7 +37,7 @@ public abstract class TestArchitectureBase : Architecture
/// <summary>
/// 初始化架构组件,注册模型、系统并设置事件监听器
/// </summary>
protected override void Init()
protected override void OnInitialize()
{
InitCalled = true;
_postRegistrationHook?.Invoke(this);

View File

@ -53,7 +53,7 @@ public class EcsAdvancedTests
{
var system = (IEcsSystem)Activator.CreateInstance(systemType)!;
((IContextAware)system).SetContext(_context!);
system.Init();
system.Initialize();
systems.Add(system);
_container.RegisterPlurality(system);
}
@ -65,7 +65,7 @@ public class EcsAdvancedTests
{
var runner = new EcsSystemRunner();
((IContextAware)runner).SetContext(_context!);
runner.Init();
runner.Initialize();
return runner;
}
@ -220,7 +220,7 @@ public class EcsAdvancedTests
foreach (var system in new[] { systemA, systemB, systemC })
{
((IContextAware)system).SetContext(_context!);
system.Init();
system.Initialize();
_container.RegisterPlurality(system);
}
@ -247,7 +247,7 @@ public class EcsAdvancedTests
var movementSystem = new MovementSystem();
((IContextAware)movementSystem).SetContext(_context!);
movementSystem.Init();
movementSystem.Initialize();
_container.RegisterPlurality(movementSystem);
_container.Register(new List<IEcsSystem> { movementSystem } as IReadOnlyList<IEcsSystem>);

View File

@ -68,7 +68,7 @@ public class EcsBasicTests
{
var system = (IEcsSystem)Activator.CreateInstance(systemType)!;
system.SetContext(_context!);
system.Init();
system.Initialize();
systems.Add(system);
_container.RegisterPlurality(system);
}

View File

@ -68,7 +68,7 @@ public class EcsIntegrationTests
{
var system = (IEcsSystem)Activator.CreateInstance(systemType)!;
system.SetContext(_context!);
system.Init();
system.Initialize();
systems.Add(system);
_container.RegisterPlurality(system);
}

View File

@ -214,7 +214,7 @@ public class MediatorComprehensiveTests
var results = new List<int>();
// 流应该在100ms后被取消TaskCanceledException 继承自 OperationCanceledException
Assert.ThrowsAsync<OperationCanceledException>(async () =>
Assert.CatchAsync<OperationCanceledException>(async () =>
{
await foreach (var item in stream)
{

View File

@ -30,8 +30,8 @@ public sealed class AsyncTestModel : AbstractModel, IAsyncInitializable
/// <exception cref="InvalidOperationException">当该方法被调用时抛出异常</exception>
public void Init()
{
// sync Init 不应该被调用
throw new InvalidOperationException("Sync Init should not be called");
// sync OnInitialize 不应该被调用
throw new InvalidOperationException("Sync OnInitialize should not be called");
}
/// <summary>

View File

@ -15,7 +15,7 @@ public sealed class FailingModel : IModel
/// 该方法会故意抛出InvalidOperationException异常
/// </summary>
/// <exception cref="InvalidOperationException">总是抛出此异常以模拟初始化失败</exception>
public void Init()
public void Initialize()
{
throw new InvalidOperationException("Model init failed intentionally");
}

View File

@ -18,7 +18,7 @@ public sealed class TestModel : AbstractModel, ITestModel
/// <summary>
/// 初始化模型
/// </summary>
public void Init()
public void Initialize()
{
Initialized = true;
}

View File

@ -114,7 +114,7 @@ public class StateMachineSystemTests
Assert.Throws<InvalidOperationException>(() => state1.GetContext());
Assert.Throws<InvalidOperationException>(() => state2.GetContext());
_stateMachine.Init();
_stateMachine.Initialize();
Assert.That(state1.GetContext(), Is.SameAs(_context));
Assert.That(state2.GetContext(), Is.SameAs(_context));
@ -129,7 +129,7 @@ public class StateMachineSystemTests
var state = new TestStateV5();
_stateMachine!.Register(state);
_stateMachine.Init();
_stateMachine.Initialize();
}
/// <summary>
@ -173,7 +173,7 @@ public class StateMachineSystemTests
_stateMachine!.Register(state1);
_stateMachine.Register(state2);
_stateMachine.Init();
_stateMachine.Initialize();
await _stateMachine.ChangeToAsync<TestStateV5>();
Assert.That(eventReceived, Is.True);
@ -203,7 +203,7 @@ public class StateMachineSystemTests
_stateMachine!.Register(state1);
_stateMachine.Register(state2);
_stateMachine.Init();
_stateMachine.Initialize();
await _stateMachine.ChangeToAsync<TestStateV5>();
eventReceived = false;
@ -230,7 +230,7 @@ public class StateMachineSystemTests
_stateMachine!.Register(state1);
_stateMachine.Register(state2);
_stateMachine.Init();
_stateMachine.Initialize();
await _stateMachine.ChangeToAsync<TestStateV5>();
await _stateMachine.ChangeToAsync<TestStateV5_2>();
await _stateMachine.ChangeToAsync<TestStateV5>();

View File

@ -30,10 +30,10 @@ public sealed class AsyncTestSystem : ISystem, IAsyncInitializable
return _context;
}
public void Init()
public void Initialize()
{
// 同步 Init 不应该被调用
throw new InvalidOperationException("Sync Init should not be called");
// 同步 OnInitialize 不应该被调用
throw new InvalidOperationException("Sync OnInitialize should not be called");
}
public void Destroy()

View File

@ -45,7 +45,7 @@ public sealed class TestSystem : ISystem
/// <summary>
/// 初始化系统
/// </summary>
public void Init()
public void Initialize()
{
Initialized = true;
}

View File

@ -61,11 +61,11 @@ public class AbstractContextUtilityTests
{
var utility = new TestContextUtilityV1();
Assert.That(utility.Initialized, Is.False, "Utility should not be initialized before Init");
Assert.That(utility.Initialized, Is.False, "Utility should not be initialized before OnInitialize");
utility.Init();
utility.Initialize();
Assert.That(utility.Initialized, Is.True, "Utility should be initialized after Init");
Assert.That(utility.Initialized, Is.True, "Utility should be initialized after OnInitialize");
}
/// <summary>
@ -76,11 +76,11 @@ public class AbstractContextUtilityTests
{
var utility = new TestContextUtilityV1();
Assert.That(utility.GetLogger(), Is.Null, "Logger should be null before Init");
Assert.That(utility.GetLogger(), Is.Null, "Logger should be null before OnInitialize");
utility.Init();
utility.Initialize();
Assert.That(utility.GetLogger(), Is.Not.Null, "Logger should be set after Init");
Assert.That(utility.GetLogger(), Is.Not.Null, "Logger should be set after OnInitialize");
}
/// <summary>
@ -91,11 +91,11 @@ public class AbstractContextUtilityTests
{
var utility = new TestContextUtilityV1();
Assert.That(utility.InitCalled, Is.False, "InitCalled should be false before Init");
Assert.That(utility.InitCalled, Is.False, "InitCalled should be false before OnInitialize");
utility.Init();
utility.Initialize();
Assert.That(utility.InitCalled, Is.True, "InitCalled should be true after Init");
Assert.That(utility.InitCalled, Is.True, "InitCalled should be true after OnInitialize");
}
/// <summary>
@ -106,7 +106,7 @@ public class AbstractContextUtilityTests
{
var utility = new TestContextUtilityV1();
utility.Init();
utility.Initialize();
Assert.That(utility.Destroyed, Is.False, "Utility should not be destroyed before Destroy");
utility.Destroy();
@ -156,7 +156,7 @@ public class AbstractContextUtilityTests
Assert.That(utility.Initialized, Is.False);
Assert.That(utility.CustomInitializationDone, Is.False);
utility.Init();
utility.Initialize();
Assert.That(utility.Initialized, Is.True);
Assert.That(utility.CustomInitializationDone, Is.True);
@ -175,7 +175,7 @@ public class AbstractContextUtilityTests
Assert.That(utility.Destroyed, Is.False);
// 初始化
utility.Init();
utility.Initialize();
Assert.That(utility.Initialized, Is.True);
Assert.That(utility.Destroyed, Is.False);
@ -194,7 +194,7 @@ public class AbstractContextUtilityTests
var utility = new TestContextUtilityV1();
// 第一次初始化和销毁
utility.Init();
utility.Initialize();
Assert.That(utility.Initialized, Is.True);
utility.Destroy();
Assert.That(utility.Destroyed, Is.True);
@ -203,7 +203,7 @@ public class AbstractContextUtilityTests
utility.Destroyed = false;
// 第二次初始化和销毁
utility.Init();
utility.Initialize();
Assert.That(utility.Initialized, Is.True);
utility.Destroy();
Assert.That(utility.Destroyed, Is.True);

View File

@ -261,15 +261,11 @@ public abstract class Architecture(
}
// 处理销毁(支持 IDestroyable 或 IAsyncDestroyable
if (component is IDestroyable or IAsyncDestroyable)
{
// 原子去重HashSet.Add 返回 true 表示添加成功(之前不存在)
if (_disposableSet.Add(component))
{
_disposables.Add(component);
_logger.Trace($"Registered {component.GetType().Name} for destruction");
}
}
if (component is not (IDestroyable or IAsyncDestroyable)) return;
// 原子去重HashSet.Add 返回 true 表示添加成功(之前不存在)
if (!_disposableSet.Add(component)) return;
_disposables.Add(component);
_logger.Trace($"Registered {component.GetType().Name} for destruction");
}
/// <summary>
@ -354,13 +350,13 @@ public abstract class Architecture(
if (asyncMode && component is IAsyncInitializable asyncInit)
await asyncInit.InitializeAsync();
else
component.Init();
component.Initialize();
}
/// <summary>
/// 抽象初始化方法,由子类重写以进行自定义初始化操作
/// </summary>
protected abstract void Init();
protected abstract void OnInitialize();
/// <summary>
/// 异步销毁架构及所有组件
@ -663,10 +659,10 @@ public abstract class Architecture(
// 执行服务钩子
Container.ExecuteServicesHook(Configurator);
// === 用户 Init ===
_logger.Debug("Calling user Init()");
Init();
_logger.Debug("User Init() completed");
// === 用户 OnInitialize ===
_logger.Debug("Calling user OnInitialize()");
OnInitialize();
_logger.Debug("User OnInitialize() completed");
// === 组件初始化阶段 ===
await InitializeAllComponentsAsync(asyncMode);

View File

@ -90,7 +90,7 @@ public class ArchitectureContext(IIocContainer container) : IArchitectureContext
var mediator = Mediator;
if (mediator == null)
throw new InvalidOperationException(
"Mediator not registered. Call EnableMediator() in your Architecture.Init() method.");
"Mediator not registered. Call EnableMediator() in your Architecture.OnInitialize() method.");
return await mediator.Send(request, cancellationToken);
}

View File

@ -1,5 +1,6 @@
using Arch.Core;
using GFramework.Core.Abstractions.ecs;
using GFramework.Core.extensions;
using GFramework.Core.system;
namespace GFramework.Core.ecs;
@ -34,7 +35,7 @@ public abstract class EcsSystemBase : AbstractSystem, IEcsSystem
/// </summary>
protected override void OnInit()
{
EcsWorld = Context.GetService<EcsWorld>() ?? throw new InvalidOperationException(
EcsWorld = this.GetService<EcsWorld>() ?? throw new InvalidOperationException(
"EcsWorld not found in context. Make sure ECS is properly initialized.");
OnEcsInit();

View File

@ -1,4 +1,5 @@
using GFramework.Core.Abstractions.ecs;
using GFramework.Core.extensions;
using GFramework.Core.system;
namespace GFramework.Core.ecs;
@ -17,8 +18,8 @@ public sealed class EcsSystemRunner : AbstractSystem
protected override void OnInit()
{
// 从容器获取所有已注册的ECS系统
var systemsList = Context.GetService<IReadOnlyList<IEcsSystem>>();
if (systemsList != null && systemsList.Count > 0)
var systemsList = this.GetService<IReadOnlyList<IEcsSystem>>();
if (systemsList is { Count: > 0 })
{
// 按优先级排序
_systems.AddRange(systemsList.OrderBy(s => s.Priority));

View File

@ -8,25 +8,24 @@ namespace GFramework.Core.ecs;
/// </summary>
public sealed class EcsWorld : IEcsWorld
{
private readonly World _world = World.Create();
private bool _disposed;
/// <summary>
/// 获取内部的Arch World实例
/// </summary>
public World InternalWorld => _world;
public World InternalWorld { get; } = World.Create();
/// <summary>
/// 当前实体数量
/// </summary>
public int EntityCount => _world.Size;
public int EntityCount => InternalWorld.Size;
/// <summary>
/// 创建一个新实体
/// </summary>
public Entity CreateEntity(params ComponentType[] types)
{
return _world.Create(types);
return InternalWorld.Create(types);
}
/// <summary>
@ -34,7 +33,7 @@ public sealed class EcsWorld : IEcsWorld
/// </summary>
public void DestroyEntity(Entity entity)
{
_world.Destroy(entity);
InternalWorld.Destroy(entity);
}
/// <summary>
@ -42,7 +41,7 @@ public sealed class EcsWorld : IEcsWorld
/// </summary>
public bool IsAlive(Entity entity)
{
return _world.IsAlive(entity);
return InternalWorld.IsAlive(entity);
}
/// <summary>
@ -50,7 +49,7 @@ public sealed class EcsWorld : IEcsWorld
/// </summary>
public void Clear()
{
_world.Clear();
InternalWorld.Clear();
}
/// <summary>
@ -60,7 +59,7 @@ public sealed class EcsWorld : IEcsWorld
{
if (_disposed) return;
World.Destroy(_world);
World.Destroy(InternalWorld);
_disposed = true;
}
}

View File

@ -1,16 +1,19 @@
namespace GFramework.Core.ecs.components;
/// <summary>
/// 位置组件
/// 位置组件,用于表示实体在二维空间中的坐标位置。
/// </summary>
public struct Position
/// <param name="x">X轴坐标值</param>
/// <param name="y">Y轴坐标值</param>
public struct Position(float x, float y)
{
public float X;
public float Y;
/// <summary>
/// 获取X轴坐标值。
/// </summary>
public float X { get; set; } = x;
public Position(float x, float y)
{
X = x;
Y = y;
}
/// <summary>
/// 获取Y轴坐标值。
/// </summary>
public float Y { get; set; } = y;
}

View File

@ -1,16 +1,19 @@
namespace GFramework.Core.ecs.components;
/// <summary>
/// 速度组件
/// 速度组件,用于表示实体在二维空间中的运动速度。
/// </summary>
public struct Velocity
/// <param name="x">X轴方向的速度分量</param>
/// <param name="y">Y轴方向的速度分量</param>
public struct Velocity(float x, float y)
{
public float X;
public float Y;
/// <summary>
/// X轴方向的速度分量
/// </summary>
public float X { get; set; } = x;
public Velocity(float x, float y)
{
X = x;
Y = y;
}
/// <summary>
/// Y轴方向的速度分量
/// </summary>
public float Y { get; set; } = y;
}

View File

@ -1,67 +0,0 @@
using Arch.Core;
using GFramework.Core.Abstractions.architecture;
using GFramework.Core.ecs.components;
namespace GFramework.Core.ecs.examples;
/// <summary>
/// ECS使用示例 - 演示如何创建和管理实体
/// </summary>
public static class EcsUsageExample
{
/// <summary>
/// 示例1: 创建移动的敌人实体
/// </summary>
public static void CreateMovingEnemies(IArchitectureContext context, int count)
{
var ecsWorld = context.GetEcsWorld();
var world = ecsWorld.InternalWorld;
for (int i = 0; i < count; i++)
{
// 创建实体
var entity = ecsWorld.CreateEntity(
new ComponentType[]
{
typeof(Position),
typeof(Velocity)
}
);
// 设置初始位置和速度
world.Set(entity, new Position(i * 10, 0));
var random = new Random();
world.Set(entity, new Velocity(
(float)(random.NextDouble() * 100 - 50),
(float)(random.NextDouble() * 100 - 50)
));
}
}
/// <summary>
/// 示例2: 批量清理实体
/// </summary>
public static void ClearAllEntities(IArchitectureContext context)
{
var ecsWorld = context.GetEcsWorld();
ecsWorld.Clear();
}
/// <summary>
/// 示例3: 查询特定实体
/// </summary>
public static int CountMovingEntities(IArchitectureContext context)
{
var ecsWorld = context.GetEcsWorld();
var world = ecsWorld.InternalWorld;
int count = 0;
var query = new QueryDescription()
.WithAll<Position, Velocity>();
world.Query(in query, (Entity entity) => { count++; });
return count;
}
}

View File

@ -4,14 +4,22 @@ using GFramework.Core.ecs.components;
namespace GFramework.Core.ecs.systems;
/// <summary>
/// 移动系统示例 - 根据速度更新位置
/// 移动系统,负责更新具有位置和速度组件的实体的位置。
/// 根据速度和时间增量计算实体的新位置。
/// </summary>
public class MovementSystem : EcsSystemBase
{
private QueryDescription _query;
/// <summary>
/// 获取系统的优先级,数值越小优先级越高。
/// </summary>
public override int Priority => 0;
/// <summary>
/// ECS初始化回调方法在系统初始化时调用。
/// 创建查询描述符用于查找同时拥有Position和Velocity组件的实体。
/// </summary>
protected override void OnEcsInit()
{
// 创建查询查找所有同时拥有Position和Velocity组件的实体
@ -19,6 +27,10 @@ public class MovementSystem : EcsSystemBase
.WithAll<Position, Velocity>();
}
/// <summary>
/// 系统更新方法,每帧调用一次。
/// </summary>
/// <param name="deltaTime">帧间隔时间,用于计算位置变化量</param>
public override void Update(float deltaTime)
{
// 查询并更新所有符合条件的实体

View File

@ -13,7 +13,7 @@ public abstract class AbstractModel : ContextAwareBase, IModel
/// <summary>
/// 初始化模型调用抽象方法OnInit执行具体初始化逻辑
/// </summary>
void IInitializable.Init()
void IInitializable.Initialize()
{
OnInit();
}

View File

@ -48,7 +48,7 @@ public class StateMachineSystem : StateMachine, IStateMachineSystem
/// 初始化方法,在系统启动时调用
/// 遍历所有状态实例为实现了IContextAware接口的状态设置上下文
/// </summary>
public virtual void Init()
public virtual void Initialize()
{
foreach (var state in States.Values.OfType<IContextAware>()) state.SetContext(_context);
}

View File

@ -17,7 +17,7 @@ public abstract class AbstractSystem : ContextAwareBase, ISystem
/// <summary>
/// 系统初始化方法,调用抽象初始化方法
/// </summary>
public void Init()
public void Initialize()
{
var name = GetType().Name;
_logger = LoggerFactoryResolver.Provider.CreateLogger(name);

View File

@ -19,7 +19,7 @@ public abstract class AbstractContextUtility : ContextAwareBase, IContextUtility
/// <summary>
/// 初始化上下文工具类
/// </summary>
public void Init()
public void Initialize()
{
var name = GetType().Name;
// 获取上下文中的日志记录器

View File

@ -240,7 +240,7 @@ public class SettingsModel<TRepository>(IDataLocationProvider? locationProvider,
: null;
}
// =========================
// Init
// OnInitialize
// =========================
/// <summary>

View File

@ -53,7 +53,7 @@ public abstract class AbstractArchitecture(
/// 初始化架构,按顺序注册模型、系统和工具。
/// 包括将架构绑定到Godot生命周期并调用模块安装逻辑。
/// </summary>
protected override void Init()
protected override void OnInitialize()
{
_architectureAnchorName =
$"__{GFrameworkConstants.FrameworkName}__{GetType().Name}__{GetHashCode()}__ArchitectureAnchor__";
@ -76,7 +76,7 @@ public abstract class AbstractArchitecture(
if (Engine.GetMainLoop() is not SceneTree tree)
return;
// 防止重复挂载(热重载 / 多次 Init
// 防止重复挂载(热重载 / 多次 OnInitialize
if (tree.Root.GetNodeOrNull(_architectureAnchorName) != null)
return;