feat(registry): 添加通用注册表基础设施并重构UI注册功能

- 新增 IKeyValue 接口定义通用键值对数据结构契约
- 创建 IRegistry 接口提供通用注册表功能定义
- 实现 KeyValueRegistryBase 基类提供基于字典的键值对管理
- 添加 GodotSceneRegistry 类管理Godot场景资源
- 重构 GodotUiRegistry 和 GodotUiFactory 使用新的注册表基类
- 移除废弃的 IWritableUiRegistry 接口
- 更新项目文件添加注册表相关目录结构
This commit is contained in:
GeWuYou 2026-01-18 16:01:10 +08:00
parent bbb8d417f6
commit 3589a0cf2f
10 changed files with 162 additions and 61 deletions

View File

@ -0,0 +1,19 @@
namespace GFramework.Core.Abstractions.bases;
/// <summary>
/// 表示键值对的接口,定义了通用的键值对数据结构契约
/// </summary>
/// <typeparam name="TKey">键的类型</typeparam>
/// <typeparam name="TValue">值的类型</typeparam>
public interface IKeyValue<out TKey, out TValue>
{
/// <summary>
/// 获取键值对中的键
/// </summary>
TKey Key { get; }
/// <summary>
/// 获取键值对中的值
/// </summary>
TValue Value { get; }
}

View File

@ -0,0 +1,38 @@
using GFramework.Core.Abstractions.bases;
namespace GFramework.Game.Abstractions.registry;
/// <summary>
/// 表示一个通用的注册表接口用于根据键类型T获取值类型TR的对象
/// </summary>
/// <typeparam name="T">注册表中用作键的类型</typeparam>
/// <typeparam name="Tr">注册表中存储的值的类型</typeparam>
public interface IRegistry<in T, Tr>
{
/// <summary>
/// 根据指定的键获取对应的值
/// </summary>
/// <param name="key">用于查找值的键</param>
/// <returns>与指定键关联的值</returns>
Tr Get(T key);
/// <summary>
/// 检查注册表是否包含指定的键
/// </summary>
/// <param name="key">要检查的键</param>
/// <returns>如果注册表包含具有指定键的元素则为true否则为false</returns>
bool Contains(T key);
/// <summary>
/// 添加一个键值对到注册表中
/// </summary>
/// <param name="mapping">要添加的键值对映射对象</param>
IRegistry<T, Tr> Registry(IKeyValue<T, Tr> mapping);
/// <summary>
/// 添加一个键值对到注册表中
/// </summary>
/// <param name="key">要添加的键</param>
/// <param name="value">要添加的值</param>
IRegistry<T, Tr> Registry(T key, Tr value);
}

View File

@ -0,0 +1,69 @@
using System.Collections.Generic;
using GFramework.Core.Abstractions.bases;
using GFramework.Game.Abstractions.registry;
namespace GFramework.Core.Abstractions.registries;
/// <summary>
/// 基于Dictionary的通用键值注册表基类
/// 提供基于字典的键值对注册、查询和管理功能
/// </summary>
/// <typeparam name="TKey">键的类型</typeparam>
/// <typeparam name="TValue">值的类型</typeparam>
public abstract class KeyValueRegistryBase<TKey, TValue>
: IRegistry<TKey, TValue>
{
/// <summary>
/// 存储键值对映射关系的字典
/// </summary>
protected readonly IDictionary<TKey, TValue> Map;
/// <summary>
/// 初始化KeyValueRegistryBase的新实例
/// </summary>
/// <param name="comparer">用于比较键的相等性的比较器如果为null则使用默认比较器</param>
protected KeyValueRegistryBase(IEqualityComparer<TKey>? comparer = null)
{
// 使用指定的比较器或默认比较器创建字典
Map = new Dictionary<TKey, TValue>(comparer ?? EqualityComparer<TKey>.Default);
}
/// <summary>
/// 根据指定的键获取对应的值
/// </summary>
/// <param name="key">要查找的键</param>
/// <returns>与键关联的值</returns>
/// <exception cref="KeyNotFoundException">当键不存在时抛出异常</exception>
public virtual TValue Get(TKey key)
=> Map.TryGetValue(key, out var value)
? value
: throw new KeyNotFoundException($"{GetType().Name}: key not registered: {key}");
/// <summary>
/// 判断是否包含指定的键
/// </summary>
/// <param name="key">要检查的键</param>
/// <returns>如果包含该键返回true否则返回false</returns>
public virtual bool Contains(TKey key)
=> Map.ContainsKey(key);
/// <summary>
/// 注册键值对到注册表中
/// </summary>
/// <param name="key">要注册的键</param>
/// <param name="value">要注册的值</param>
/// <returns>当前注册表实例,支持链式调用</returns>
public virtual IRegistry<TKey, TValue> Registry(TKey key, TValue value)
{
Map.Add(key, value);
return this;
}
/// <summary>
/// 注册键值对映射对象到注册表中
/// </summary>
/// <param name="mapping">包含键值对的映射对象</param>
/// <returns>当前注册表实例,支持链式调用</returns>
public virtual IRegistry<TKey, TValue> Registry(IKeyValue<TKey, TValue> mapping)
=> Registry(mapping.Key, mapping.Value);
}

View File

@ -0,0 +1,9 @@
using GFramework.Game.Abstractions.registry;
namespace GFramework.Game.Abstractions.scene;
/// <summary>
/// 游戏场景注册表接口,用于管理游戏场景的注册和查找
/// </summary>
/// <typeparam name="T">场景类型,表示注册表中存储的具体场景对象类型</typeparam>
public interface IGameSceneRegistry<T> : IRegistry<string, T>;

View File

@ -1,4 +1,5 @@
using GFramework.Core.Abstractions.utility;
using GFramework.Game.Abstractions.registry;
namespace GFramework.Game.Abstractions.ui;
@ -6,12 +7,4 @@ namespace GFramework.Game.Abstractions.ui;
/// UI注册表接口用于根据UI键获取对应的UI实例
/// </summary>
/// <typeparam name="T">UI实例的类型参数使用协变修饰符out</typeparam>
public interface IUiRegistry<out T> : IUtility
{
/// <summary>
/// 根据指定的UI键获取对应的UI实例
/// </summary>
/// <param name="uiKey">UI的唯一标识键</param>
/// <returns>与指定键关联的UI实例</returns>
T Get(string uiKey);
}
public interface IUiRegistry<T> : IUtility, IRegistry<string, T>;

View File

@ -1,16 +0,0 @@
namespace GFramework.Game.Abstractions.ui;
/// <summary>
/// 可写的UI注册表接口
/// </summary>
/// <typeparam name="T">UI实例的类型参数</typeparam>
public interface IWritableUiRegistry<T> : IUiRegistry<T> where T : class
{
/// <summary>
/// 注册UI实例到注册表
/// </summary>
/// <param name="key">UI的唯一标识键</param>
/// <param name="scene">要注册的UI实例</param>
/// <returns>当前注册表实例</returns>
IWritableUiRegistry<T> Register(string key, T scene);
}

View File

@ -13,4 +13,7 @@
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4"/>
</ItemGroup>
<ItemGroup>
<Folder Include="registry\"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,13 @@
using GFramework.Core.Abstractions.registries;
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.scene;
/// <summary>
/// Godot场景注册表类
/// 继承自KeyValueRegistryBase使用字符串作为键PackedScene作为值进行存储
/// 实现IUiRegistry接口提供UI场景的注册和管理功能
/// </summary>
public class GodotSceneRegistry() : KeyValueRegistryBase<string, PackedScene>(StringComparer.Ordinal),
IUiRegistry<PackedScene>;

View File

@ -14,7 +14,7 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
/// <summary>
/// UI注册表用于存储和获取PackedScene类型的UI资源
/// </summary>
private IWritableUiRegistry<PackedScene> _registry = null!;
private IUiRegistry<PackedScene> _registry = null!;
/// <summary>
/// 根据指定的UI键创建UI页面实例
@ -44,6 +44,6 @@ public class GodotUiFactory : AbstractContextUtility, IUiFactory
/// </summary>
protected override void OnInit()
{
_registry = this.GetUtility<IWritableUiRegistry<PackedScene>>()!;
_registry = this.GetUtility<IUiRegistry<PackedScene>>()!;
}
}

View File

@ -1,40 +1,13 @@
using GFramework.Game.Abstractions.ui;
using GFramework.Core.Abstractions.registries;
using GFramework.Game.Abstractions.ui;
using Godot;
namespace GFramework.Godot.ui;
/// <summary>
/// Godot UI注册表实现类用于管理PackedScene类型的UI资源注册和获取
/// Godot UI注册表类用于管理UI相关的PackedScene资源
/// 继承自KeyValueRegistryBase使用字符串作为键PackedScene作为值进行存储
/// 实现IUiRegistry接口提供UI场景的注册和管理功能
/// </summary>
public class GodotUiRegistry : IWritableUiRegistry<PackedScene>
{
/// <summary>
/// 存储UI键值对的字典键为UI标识符值为对应的PackedScene对象
/// </summary>
private readonly Dictionary<string, PackedScene> _map = new(StringComparer.Ordinal);
/// <summary>
/// 注册UI场景到注册表中
/// </summary>
/// <param name="key">UI的唯一标识符</param>
/// <param name="scene">要注册的PackedScene对象</param>
/// <returns>返回当前UI注册表实例支持链式调用</returns>
public IWritableUiRegistry<PackedScene> Register(string key, PackedScene scene)
{
_map[key] = scene;
return this;
}
/// <summary>
/// 根据键获取已注册的UI场景
/// </summary>
/// <param name="uiKey">UI的唯一标识符</param>
/// <exception cref="KeyNotFoundException">当指定的键未找到对应的UI场景时抛出异常</exception>
/// <returns>对应的PackedScene对象</returns>
public PackedScene Get(string uiKey)
{
return !_map.TryGetValue(uiKey, out var scene)
? throw new KeyNotFoundException($"UI not registered: {uiKey}")
: scene;
}
}
public class GodotUiRegistry() : KeyValueRegistryBase<string, PackedScene>(StringComparer.Ordinal),
IUiRegistry<PackedScene>;