refactor(architecture): 简化模块注册接口并增强配置管理

- 移除 RegisterBuiltInModules 方法中的 ArchitectureProperties 参数
- 更新 ArchitectureModuleRegistry 使用 ConcurrentDictionary 存储模块工厂
- 实现模块注册的幂等性检查,相同模块名只注册一次
- 为 ArchEcsModule 添加 ArchOptions 配置类支持
- 更新 UseArch 扩展方法传递配置选项给 ArchEcsModule
- 移除废弃的 properties 命名空间引用
- 添加显式注册集成测试验证模块配置功能
This commit is contained in:
GeWuYou 2026-03-08 20:47:26 +08:00
parent ac2a9759e1
commit 5c5525e3e9
9 changed files with 190 additions and 35 deletions

View File

@ -1,3 +1,5 @@
using System.Collections.Concurrent;
namespace GFramework.Core.Abstractions.architecture;
/// <summary>
@ -5,15 +7,20 @@ namespace GFramework.Core.Abstractions.architecture;
/// </summary>
public static class ArchitectureModuleRegistry
{
private static readonly List<Func<IServiceModule>> _factories = [];
private static readonly ConcurrentDictionary<string, Func<IServiceModule>> _factories = new();
/// <summary>
/// 注册模块工厂
/// 注册模块工厂(幂等操作,相同模块名只会注册一次)
/// </summary>
/// <param name="factory">模块工厂函数</param>
public static void Register(Func<IServiceModule> factory)
{
_factories.Add(factory);
// 创建临时实例以获取模块名(用于幂等性检查)
var tempModule = factory();
var moduleName = tempModule.ModuleName;
// 幂等注册:相同模块名只注册一次
_factories.TryAdd(moduleName, factory);
}
/// <summary>
@ -22,7 +29,7 @@ public static class ArchitectureModuleRegistry
/// <returns>模块实例集合</returns>
public static IEnumerable<IServiceModule> CreateModules()
{
return _factories.Select(f => f());
return _factories.Values.Select(f => f());
}
/// <summary>

View File

@ -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
/// 注册内置的服务模块。
/// </summary>
/// <param name="container">IoC容器实例用于解析依赖。</param>
/// <param name="properties">架构属性配置,用于模块初始化。</param>
void RegisterBuiltInModules(IIocContainer container, ArchitectureProperties properties);
void RegisterBuiltInModules(IIocContainer container);
/// <summary>
/// 获取所有已注册的服务模块。

View File

@ -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);
}
/// <summary>
@ -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));
}

View File

@ -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<IEnvironment>())

View File

@ -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
/// <summary>
/// 注册内置服务模块,并根据优先级排序后完成服务注册。
/// 内置模块包括事件总线、命令执行器、查询执行器等核心模块。
/// 同时注册通过 ModuleInitializer 自动注册的外部模块。
/// 同时注册通过 ArchitectureModuleRegistry 自动注册的外部模块。
/// </summary>
/// <param name="container">IoC容器实例用于模块服务注册。</param>
/// <param name="properties">架构属性配置。</param>
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);

View File

@ -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;
/// <summary>
/// 显式注册集成测试
/// </summary>
[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;
/// <summary>
/// 测试 Arch ECS 模块显式注册
/// </summary>
[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<World>();
Assert.That(world, Is.Not.Null, "World should be registered by ArchEcsModule");
// 验证 IArchEcsModule 已注册
var ecsModule = architecture.Context.GetService<IArchEcsModule>();
Assert.That(ecsModule, Is.Not.Null, "IArchEcsModule should be registered");
}
/// <summary>
/// 测试 World 是否正确注册到容器
/// </summary>
[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<World>();
Assert.That(world, Is.Not.Null, "World should be registered in container");
}
/// <summary>
/// 测试带配置的注册
/// </summary>
[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");
}
/// <summary>
/// 测试链式调用
/// </summary>
[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<World>();
Assert.That(world, Is.Not.Null, "World should be registered");
var ecsModule = architecture.Context.GetService<IArchEcsModule>();
Assert.That(ecsModule, Is.Not.Null, "IArchEcsModule should be registered");
}
/// <summary>
/// 测试架构类,用于测试
/// </summary>
private class TestArchitecture : Architecture
{
public TestArchitecture() : base(new ArchitectureConfiguration())
{
}
protected override void OnInitialize()
{
// 测试架构,无需额外初始化
}
}
}

View File

@ -8,6 +8,7 @@ namespace GFramework.Ecs.Arch;
/// </summary>
public sealed class ArchEcsModule : IArchEcsModule
{
private readonly ArchOptions _options;
private IIocContainer? _container;
private bool _isInitialized;
private IReadOnlyList<ArchSystemAdapter<float>> _systems = [];
@ -16,9 +17,11 @@ public sealed class ArchEcsModule : IArchEcsModule
/// <summary>
/// 构造函数
/// </summary>
/// <param name="options">配置选项</param>
/// <param name="enabled">是否启用模块</param>
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
/// <summary>
/// 模块优先级
/// </summary>
public int Priority => 50;
public int Priority => _options.Priority;
/// <summary>
/// 是否启用
@ -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);
}

View File

@ -0,0 +1,22 @@
namespace GFramework.Ecs.Arch;
/// <summary>
/// Arch ECS 模块配置选项
/// </summary>
public sealed class ArchOptions
{
/// <summary>
/// World 初始容量默认1000
/// </summary>
public int WorldCapacity { get; set; } = 1000;
/// <summary>
/// 是否启用统计信息默认false
/// </summary>
public bool EnableStatistics { get; set; } = false;
/// <summary>
/// 模块优先级默认50
/// </summary>
public int Priority { get; set; } = 50;
}

View File

@ -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;
}