初学者,自用笔记

通过中央注册表/容器来获取服务对象的设计模式:业务代码不直接 new 依赖,而是向“服务定位器”按类型或键去 查询并取得所需服务实例

注册表

一个集中式的数据结构保存“名字/类型 → 对象/工厂/配置”的映射

静态注册表简单例子

public static class SkillFactoryRegistry
{
    private static readonly Dictionary<SkillType, ISkillFactory> factories = new();
 
    static SkillFactoryRegistry()
    {
        RegisterFactory(new SwapPositionSkillFactory());
        RegisterFactory(new SwapScaleSkillFactory());
        RegisterFactory(new SwapColliderSkillFactory());
        RegisterFactory(new SwapTargetSkillFactory());
        RegisterFactory(new SwapPlayerSkillFactory());
    }
 
    public static void RegisterFactory(ISkillFactory factory)
    {
        if (factory == null) return;
        factories[factory.SupportType] = factory;
    }
 
    public static ISkillFactory GetFactory(SkillType type)
    {
        factories.TryGetValue(type, out var f);
        return f;
    }
 
}



DI注册表简单例子

public interface ISkillFactory
{
    SkillType SupportType { get; }
    ISkill Create();
}

public interface ISkillFactoryRegistry
{
    ISkillFactory GetFactory(SkillType type);
    bool TryGetFactory(SkillType type, out ISkillFactory factory);
}

public sealed class SkillFactoryRegistry : ISkillFactoryRegistry
{
    private readonly IReadOnlyDictionary<SkillType, ISkillFactory> _map;

    // DI 容器注入所有 ISkillFactory 的实现(VContainer 支持注入 IEnumerable<T>)
    public SkillFactoryRegistry(IEnumerable<ISkillFactory> factories)
    {
        // 顺便做重复注册检测,避免“后注册覆盖前注册”导致隐蔽 bug
        _map = factories
            .GroupBy(f => f.SupportType)
            .ToDictionary(
                g => g.Key,
                g =>
                {
                    if (g.Count() > 1)
                        throw new InvalidOperationException($"Duplicate skill factory for type: {g.Key}");
                    return g.First();
                });
    }

    public ISkillFactory GetFactory(SkillType type)
    {
        if (!_map.TryGetValue(type, out var f))
            throw new KeyNotFoundException($"No skill factory registered for type: {type}");
        return f;
    }

    public bool TryGetFactory(SkillType type, out ISkillFactory factory)
        => _map.TryGetValue(type, out factory);
}

bootstrapper(引导器)

程序启动时最先执行的一段“组装代码”

例如,处理单例生命周期问题

全局容器例子

1.RootLifetimeScope:搭架子+注册单例(注册表)

//游戏的全局根作用域 (Global Container)
//负责所有单例系统、基础服务的注册与生命周期管理
// LifetimeScope是vc在 Unity 场景/对象层级中创建并管理一个 DI 容器的作用域
public class RootLifetimeScope : LifetimeScope
{
    // 利用 Unity 特性,在场景加载前自动创建 DI 根节点
    // 这样就无需在场景里手动拖拽预制体,实现真正的 Zero-Scene-Dependency
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void AutoInitialize()
    {
        var go = new GameObject("[RootLifetimeScope]");
        DontDestroyOnLoad(go);
        go.AddComponent<RootLifetimeScope>();
    }

    protected override void Configure(IContainerBuilder builder)
    {
        Debug.Log("[RootScope] 开始注册全局服务...");

        // 1. 注册核心基础服务 (以单例形式)
        // 意味着任何地方请求 IManager,容器都会给同一个 Manager 实例
        builder.Register<IManager, Manager>(Lifetime.Singleton);

        // 2. 注册 Bootstrapper 启动器
        // RegisterEntryPoint 会告诉 VContainer,构建完成后自动调用它的 StartAsync
        builder.RegisterEntryPoint<GameBootstrapper>();

        // 3. 构建回调:将 DI 容器桥接给静态 ServiceLocator
        // 这样即使是不支持注入的纯静态类,也能通过 ServiceLocator.Get<IEventBus>() 拿到单例
        builder.RegisterBuildCallback(resolver =>
        {
            ServiceLocator.Initialize(resolver);
        });
    }
}

2.GameBootstrapper:按顺序启动这些单例服务(编排启动流程)

//游戏全局启动器
//负责编排游戏启动时的各个异步流程(热更、读表、登录等)

public class GameBootstrapper : IAsyncStartable
{
    //IAsyncStartable 用来让某个类在 DI 容器构建完成后,由 VContainer 自动调用一个异步的启动入口

    private readonly IManager _Manager;//仅作示例

    // 1. 通过构造函数,VContainer 会自动把这些核心系统注入进来
    [Inject]
    public GameBootstrapper(
        IManager Manager)
    {
        _Manager = Manager;
    }

    // 2. 容器构建完毕后,自动触发 StartAsync
    public async UniTask StartAsync(CancellationToken cancellation)
    {
        Debug.Log("[Bootstrapper] 游戏启动序列开始...");

        // 初始化基础系统(如本地化、日志)
        _Manager.Initialize();

        // 初始化系统2
        await _Manager.Initialize();
        Debug.Log("[Bootstrapper] 资源更新完成.");

        // 进入主界面 / 登录场景
        EnterLoginScene();
    }

    private void EnterLoginScene()
    {
        Debug.Log("[Bootstrapper] 进入登录流程...");
        // 触发全局事件,UI系统监听到后打开登录面板

    }
}

3.ServiceLocator:把 DI 容器暴露成静态入口(给遗留/静态代码用)

//全局服务定位器(作为 DI 框架的静态桥接)
//仅供无法使用依赖注入的遗留代码或纯静态类使用
public static class ServiceLocator
{
    //用于从 VContainer 容器里获取已经注册过的对象
    private static IObjectResolver _container;

    // 由 Bootstrapper 在框架构建完成后调用初始化
    public static void Initialize(IObjectResolver container)
    {
        _container = container;
    }

    public static T Get<T>()
    {
        if (_container == null)
        {
            Debug.LogError($"[ServiceLocator] 容器未初始化,无法获取 {typeof(T).Name}");
            return default;
        }
        return _container.Resolve<T>();
    }

    public static void Dispose()
    {
        _container = null;
    }
}

场景(局部)容器例子

进入场景时注册,离开时注销

LifetimeScope

// 战斗场景的作用域容器
public class BattleLifetimeScope : LifetimeScope
{
    // 如果场景里有已经摆好的物体(比如摄像机、UI画布),可以在面板上拖进来
    [SerializeField] private Camera _battleCamera;

    protected override void Configure(IContainerBuilder builder)
    {
        Debug.Log("[BattleScope] 正在注册战斗场景专属服务...");

        // 1. 注册场景级单例逻辑 (Lifetime.Scoped)
        //Lifetime.Scoped 表示在这个场景内它是单例,场景销毁它就销毁。
        builder.RegisterEntryPoint<EnemyManager>(Lifetime.Scoped);

        // 2. 注册场景启动器
        builder.RegisterEntryPoint<BattleBootstrapper>();

        // 3. 注册场景里实体的 MonoBehaviour 组件
        // 这样其它非 Mono 的 C# 类也能直接 [Inject] 拿到这个摄像机
        if (_battleCamera != null)
        {
            builder.RegisterComponent(_battleCamera);
        }
    }
}

Bootstrapper

// 战斗场景的入口点,负责编排战斗初始化流程
public class BattleBootstrapper : IAsyncStartable
{
    private readonly EnemyManager _enemyManager;
    private readonly IManager _globalManager;
 
    // 它会在当前场景寻找 EnemyManager,同时去父节点(Root)寻找 IManager,一起注入进来
    [Inject]
    public BattleBootstrapper(EnemyManager enemyManager, IManager globalManager)
    {
        _enemyManager = enemyManager;// 仅作示例
        _globalManager = globalManager;
    }

    public async UniTask StartAsync(CancellationToken cancellation)
    {
        Debug.Log("[BattleBootstrapper] 战斗场景开始初始化...");

        // 1. 调用全局服务
        _globalManager.Initialize();

        // 2. 异步加载场景资源(假装耗时1秒)
        await UniTask.Delay(1000, cancellationToken: cancellation);

        // 3. 启动场景专属的管理器
        _enemyManager.StartSpawning();// 自己写的方法

        Debug.Log("[BattleBootstrapper] 战斗正式开始");
    }
}