From 5c5525e3e9d8c3cc483d236b4847947cbf15d0d4 Mon Sep 17 00:00:00 2001 From: GeWuYou <95328647+GeWuYou@users.noreply.github.com> Date: Sun, 8 Mar 2026 20:47:26 +0800 Subject: [PATCH] =?UTF-8?q?refactor(architecture):=20=E7=AE=80=E5=8C=96?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E6=B3=A8=E5=86=8C=E6=8E=A5=E5=8F=A3=E5=B9=B6?= =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E9=85=8D=E7=BD=AE=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 RegisterBuiltInModules 方法中的 ArchitectureProperties 参数 - 更新 ArchitectureModuleRegistry 使用 ConcurrentDictionary 存储模块工厂 - 实现模块注册的幂等性检查,相同模块名只注册一次 - 为 ArchEcsModule 添加 ArchOptions 配置类支持 - 更新 UseArch 扩展方法传递配置选项给 ArchEcsModule - 移除废弃的 properties 命名空间引用 - 添加显式注册集成测试验证模块配置功能 --- .../ArchitectureModuleRegistry.cs | 15 +- .../architecture/IServiceModuleManager.cs | 4 +- .../architecture/ArchitectureServicesTests.cs | 22 +-- GFramework.Core/architecture/Architecture.cs | 4 +- .../services/ServiceModuleManager.cs | 8 +- .../integration/ExplicitRegistrationTests.cs | 135 ++++++++++++++++++ GFramework.Ecs.Arch/ArchEcsModule.cs | 11 +- GFramework.Ecs.Arch/ArchOptions.cs | 22 +++ .../extensions/ArchExtensions.cs | 4 +- 9 files changed, 190 insertions(+), 35 deletions(-) create mode 100644 GFramework.Ecs.Arch.Tests/integration/ExplicitRegistrationTests.cs create mode 100644 GFramework.Ecs.Arch/ArchOptions.cs diff --git a/GFramework.Core.Abstractions/architecture/ArchitectureModuleRegistry.cs b/GFramework.Core.Abstractions/architecture/ArchitectureModuleRegistry.cs index 1259545..ab5a7c3 100644 --- a/GFramework.Core.Abstractions/architecture/ArchitectureModuleRegistry.cs +++ b/GFramework.Core.Abstractions/architecture/ArchitectureModuleRegistry.cs @@ -1,3 +1,5 @@ +using System.Collections.Concurrent; + namespace GFramework.Core.Abstractions.architecture; /// @@ -5,15 +7,20 @@ namespace GFramework.Core.Abstractions.architecture; /// public static class ArchitectureModuleRegistry { - private static readonly List> _factories = []; + private static readonly ConcurrentDictionary> _factories = new(); /// - /// 注册模块工厂 + /// 注册模块工厂(幂等操作,相同模块名只会注册一次) /// /// 模块工厂函数 public static void Register(Func factory) { - _factories.Add(factory); + // 创建临时实例以获取模块名(用于幂等性检查) + var tempModule = factory(); + var moduleName = tempModule.ModuleName; + + // 幂等注册:相同模块名只注册一次 + _factories.TryAdd(moduleName, factory); } /// @@ -22,7 +29,7 @@ public static class ArchitectureModuleRegistry /// 模块实例集合 public static IEnumerable CreateModules() { - return _factories.Select(f => f()); + return _factories.Values.Select(f => f()); } /// diff --git a/GFramework.Core.Abstractions/architecture/IServiceModuleManager.cs b/GFramework.Core.Abstractions/architecture/IServiceModuleManager.cs index 20b7d52..8c7face 100644 --- a/GFramework.Core.Abstractions/architecture/IServiceModuleManager.cs +++ b/GFramework.Core.Abstractions/architecture/IServiceModuleManager.cs @@ -1,5 +1,4 @@ using GFramework.Core.Abstractions.ioc; -using GFramework.Core.Abstractions.properties; namespace GFramework.Core.Abstractions.architecture; @@ -18,8 +17,7 @@ public interface IServiceModuleManager /// 注册内置的服务模块。 /// /// IoC容器实例,用于解析依赖。 - /// 架构属性配置,用于模块初始化。 - void RegisterBuiltInModules(IIocContainer container, ArchitectureProperties properties); + void RegisterBuiltInModules(IIocContainer container); /// /// 获取所有已注册的服务模块。 diff --git a/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs b/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs index 936204b..5621b33 100644 --- a/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs +++ b/GFramework.Core.Tests/architecture/ArchitectureServicesTests.cs @@ -4,7 +4,6 @@ using GFramework.Core.Abstractions.environment; using GFramework.Core.Abstractions.events; using GFramework.Core.Abstractions.ioc; using GFramework.Core.Abstractions.model; -using GFramework.Core.Abstractions.properties; using GFramework.Core.Abstractions.query; using GFramework.Core.Abstractions.system; using GFramework.Core.Abstractions.utility; @@ -47,8 +46,7 @@ public class ArchitectureServicesTests private void RegisterBuiltInServices() { - var properties = new ArchitectureProperties(); - _services!.ModuleManager.RegisterBuiltInModules(_services.Container, properties); + _services!.ModuleManager.RegisterBuiltInModules(_services.Container); } /// @@ -215,13 +213,11 @@ public class ArchitectureServicesTests [Test] public void Multiple_Instances_Should_Have_Independent_EventBus() { - var properties = new ArchitectureProperties(); - var services1 = new ArchitectureServices(); - services1.ModuleManager.RegisterBuiltInModules(services1.Container, properties); + services1.ModuleManager.RegisterBuiltInModules(services1.Container); var services2 = new ArchitectureServices(); - services2.ModuleManager.RegisterBuiltInModules(services2.Container, properties); + services2.ModuleManager.RegisterBuiltInModules(services2.Container); Assert.That(services1.EventBus, Is.Not.SameAs(services2.EventBus)); } @@ -232,13 +228,11 @@ public class ArchitectureServicesTests [Test] public void Multiple_Instances_Should_Have_Independent_CommandBus() { - var properties = new ArchitectureProperties(); - var services1 = new ArchitectureServices(); - services1.ModuleManager.RegisterBuiltInModules(services1.Container, properties); + services1.ModuleManager.RegisterBuiltInModules(services1.Container); var services2 = new ArchitectureServices(); - services2.ModuleManager.RegisterBuiltInModules(services2.Container, properties); + services2.ModuleManager.RegisterBuiltInModules(services2.Container); Assert.That(services1.CommandExecutor, Is.Not.SameAs(services2.CommandExecutor)); } @@ -249,13 +243,11 @@ public class ArchitectureServicesTests [Test] public void Multiple_Instances_Should_Have_Independent_QueryBus() { - var properties = new ArchitectureProperties(); - var services1 = new ArchitectureServices(); - services1.ModuleManager.RegisterBuiltInModules(services1.Container, properties); + services1.ModuleManager.RegisterBuiltInModules(services1.Container); var services2 = new ArchitectureServices(); - services2.ModuleManager.RegisterBuiltInModules(services2.Container, properties); + services2.ModuleManager.RegisterBuiltInModules(services2.Container); Assert.That(services1.QueryExecutor, Is.Not.SameAs(services2.QueryExecutor)); } diff --git a/GFramework.Core/architecture/Architecture.cs b/GFramework.Core/architecture/Architecture.cs index 717c17e..0ebdff6 100644 --- a/GFramework.Core/architecture/Architecture.cs +++ b/GFramework.Core/architecture/Architecture.cs @@ -641,8 +641,8 @@ public abstract class Architecture( _logger = LoggerFactoryResolver.Provider.CreateLogger(GetType().Name); Environment.Initialize(); - // 注册内置服务模块(根据配置) - Services.ModuleManager.RegisterBuiltInModules(Container, Configuration.ArchitectureProperties); + // 注册内置服务模块 + Services.ModuleManager.RegisterBuiltInModules(Container); // 将 Environment 注册到容器(如果尚未注册) if (!Container.Contains()) diff --git a/GFramework.Core/services/ServiceModuleManager.cs b/GFramework.Core/services/ServiceModuleManager.cs index 68bd2c0..9ed0aac 100644 --- a/GFramework.Core/services/ServiceModuleManager.cs +++ b/GFramework.Core/services/ServiceModuleManager.cs @@ -2,7 +2,6 @@ using GFramework.Core.Abstractions.architecture; using GFramework.Core.Abstractions.ioc; using GFramework.Core.Abstractions.lifecycle; using GFramework.Core.Abstractions.logging; -using GFramework.Core.Abstractions.properties; using GFramework.Core.logging; using GFramework.Core.services.modules; @@ -44,11 +43,10 @@ public sealed class ServiceModuleManager : IServiceModuleManager /// /// 注册内置服务模块,并根据优先级排序后完成服务注册。 /// 内置模块包括事件总线、命令执行器、查询执行器等核心模块。 - /// 同时注册通过 ModuleInitializer 自动注册的外部模块。 + /// 同时注册通过 ArchitectureModuleRegistry 自动注册的外部模块。 /// /// IoC容器实例,用于模块服务注册。 - /// 架构属性配置。 - public void RegisterBuiltInModules(IIocContainer container, ArchitectureProperties properties) + public void RegisterBuiltInModules(IIocContainer container) { if (_builtInModulesRegistered) { @@ -62,7 +60,7 @@ public sealed class ServiceModuleManager : IServiceModuleManager RegisterModule(new QueryExecutorModule()); RegisterModule(new AsyncQueryExecutorModule()); - // 注册外部模块(通过 ModuleInitializer 自动注册) + // 注册外部模块(通过 ArchitectureModuleRegistry 自动注册) foreach (var module in ArchitectureModuleRegistry.CreateModules()) { RegisterModule(module); diff --git a/GFramework.Ecs.Arch.Tests/integration/ExplicitRegistrationTests.cs b/GFramework.Ecs.Arch.Tests/integration/ExplicitRegistrationTests.cs new file mode 100644 index 0000000..515e5c9 --- /dev/null +++ b/GFramework.Ecs.Arch.Tests/integration/ExplicitRegistrationTests.cs @@ -0,0 +1,135 @@ +using Arch.Core; +using GFramework.Core.Abstractions.architecture; +using GFramework.Core.architecture; +using GFramework.Core.ioc; +using GFramework.Ecs.Arch.Abstractions; +using GFramework.Ecs.Arch.extensions; + +namespace GFramework.Ecs.Arch.Tests.integration; + +/// +/// 显式注册集成测试 +/// +[TestFixture] +public class ExplicitRegistrationTests +{ + [SetUp] + public void Setup() + { + _container = new MicrosoftDiContainer(); + _context = new ArchitectureContext(_container); + + // 清空注册表,确保测试隔离 + ArchitectureModuleRegistry.Clear(); + } + + [TearDown] + public void TearDown() + { + _container?.Clear(); + _context = null; + ArchitectureModuleRegistry.Clear(); + GameContext.Clear(); + } + + private MicrosoftDiContainer? _container; + private ArchitectureContext? _context; + + /// + /// 测试 Arch ECS 模块显式注册 + /// + [Test] + public void ArchEcsModule_Should_Be_Explicitly_Registered() + { + // Arrange + var architecture = new TestArchitecture(); + + // Act - 显式注册 + architecture.UseArch(); + architecture.Initialize(); + + // Assert - 验证 World 已注册(由 ArchEcsModule 注册) + var world = architecture.Context.GetService(); + Assert.That(world, Is.Not.Null, "World should be registered by ArchEcsModule"); + + // 验证 IArchEcsModule 已注册 + var ecsModule = architecture.Context.GetService(); + Assert.That(ecsModule, Is.Not.Null, "IArchEcsModule should be registered"); + } + + /// + /// 测试 World 是否正确注册到容器 + /// + [Test] + public void World_Should_Be_Registered_In_Container() + { + // Arrange + var architecture = new TestArchitecture(); + + // Act - 显式注册 + architecture.UseArch(); + architecture.Initialize(); + + // Assert + var world = architecture.Context.GetService(); + Assert.That(world, Is.Not.Null, "World should be registered in container"); + } + + /// + /// 测试带配置的注册 + /// + [Test] + public void UseArch_Should_Accept_Configuration() + { + // Arrange + var architecture = new TestArchitecture(); + var configCalled = false; + + // Act + architecture.UseArch(options => + { + options.WorldCapacity = 2000; + options.EnableStatistics = true; + configCalled = true; + }); + + // Assert + Assert.That(configCalled, Is.True, "Configuration delegate should be called"); + } + + /// + /// 测试链式调用 + /// + [Test] + public void UseArch_Should_Support_Chaining() + { + // Arrange & Act + var architecture = new TestArchitecture() + .UseArch() + .UseArch(options => options.WorldCapacity = 2000); + + architecture.Initialize(); + + // Assert - 验证模块已注册 + var world = architecture.Context.GetService(); + Assert.That(world, Is.Not.Null, "World should be registered"); + + var ecsModule = architecture.Context.GetService(); + Assert.That(ecsModule, Is.Not.Null, "IArchEcsModule should be registered"); + } + + /// + /// 测试架构类,用于测试 + /// + private class TestArchitecture : Architecture + { + public TestArchitecture() : base(new ArchitectureConfiguration()) + { + } + + protected override void OnInitialize() + { + // 测试架构,无需额外初始化 + } + } +} \ No newline at end of file diff --git a/GFramework.Ecs.Arch/ArchEcsModule.cs b/GFramework.Ecs.Arch/ArchEcsModule.cs index bbbdb94..31ab8e9 100644 --- a/GFramework.Ecs.Arch/ArchEcsModule.cs +++ b/GFramework.Ecs.Arch/ArchEcsModule.cs @@ -8,6 +8,7 @@ namespace GFramework.Ecs.Arch; /// public sealed class ArchEcsModule : IArchEcsModule { + private readonly ArchOptions _options; private IIocContainer? _container; private bool _isInitialized; private IReadOnlyList> _systems = []; @@ -16,9 +17,11 @@ public sealed class ArchEcsModule : IArchEcsModule /// /// 构造函数 /// + /// 配置选项 /// 是否启用模块 - public ArchEcsModule(bool enabled = true) + public ArchEcsModule(ArchOptions? options = null, bool enabled = true) { + _options = options ?? new ArchOptions(); IsEnabled = enabled; } @@ -30,7 +33,7 @@ public sealed class ArchEcsModule : IArchEcsModule /// /// 模块优先级 /// - public int Priority => 50; + public int Priority => _options.Priority; /// /// 是否启用 @@ -49,8 +52,8 @@ public sealed class ArchEcsModule : IArchEcsModule // 注册模块自身 container.RegisterPlurality(this); - // 创建并注册 World - _world = World.Create(); + // 创建并注册 World(使用配置的容量) + _world = World.Create(_options.WorldCapacity); container.Register(_world); } diff --git a/GFramework.Ecs.Arch/ArchOptions.cs b/GFramework.Ecs.Arch/ArchOptions.cs new file mode 100644 index 0000000..adc17e4 --- /dev/null +++ b/GFramework.Ecs.Arch/ArchOptions.cs @@ -0,0 +1,22 @@ +namespace GFramework.Ecs.Arch; + +/// +/// Arch ECS 模块配置选项 +/// +public sealed class ArchOptions +{ + /// + /// World 初始容量(默认:1000) + /// + public int WorldCapacity { get; set; } = 1000; + + /// + /// 是否启用统计信息(默认:false) + /// + public bool EnableStatistics { get; set; } = false; + + /// + /// 模块优先级(默认:50) + /// + public int Priority { get; set; } = 50; +} \ No newline at end of file diff --git a/GFramework.Ecs.Arch/extensions/ArchExtensions.cs b/GFramework.Ecs.Arch/extensions/ArchExtensions.cs index 811f546..dda51b1 100644 --- a/GFramework.Ecs.Arch/extensions/ArchExtensions.cs +++ b/GFramework.Ecs.Arch/extensions/ArchExtensions.cs @@ -23,8 +23,8 @@ public static class ArchExtensions var options = new ArchOptions(); configure?.Invoke(options); - // 注册模块 - ArchitectureModuleRegistry.Register(() => new ArchEcsModule(enabled: true)); + // 注册模块(传递配置选项) + ArchitectureModuleRegistry.Register(() => new ArchEcsModule(options, enabled: true)); return architecture; }