Unity轻量级模块化管理器架构:解决单例管理器泛滥问题,实现模块统一管控

12 阅读7分钟

在Unity游戏开发中,我们总会编写各种各样的管理器,比如UI管理器、音频管理器、资源管理器、状态机管理器等。传统开发中,绝大多数开发者会为每个Manager单独实现一套单例模式,这会导致多个核心痛点:

  • 代码冗余严重:每个管理器都要重复写单例判断、实例创建、销毁逻辑,大量重复样板代码;
  • 管控混乱:全局单例管理器泛滥,模块初始化、卸载、资源释放无统一入口。代码混乱,出现到处都是单例调用的混乱感,无法统一各个管理器
  • 扩展性极差:新增业务模块需要重复编写单例逻辑,模块依赖、生命周期无法统一管理。

一、架构整体设计思路

这套架构采用静态类统一管理和暴露接口实例 + 统一模块生命周期 + 模块管理器管理所有模块接口的设计思想,核心分为四层结构,职责完全解耦:

  1. IModule 核心接口:定义所有业务模块的统一生命周期规范,强制模块实现初始化、销毁方法;
  2. ModuleBase 模块基类:实现接口默认逻辑,所有业务管理器统一继承该基类,无需重复实现生命周期;
  3. ModuleManager 全局模块管理器:架构核心,线程安全单例,负责所有模块的注册、查找、卸载、全局清空;
  4. CoreUtils 静态工具入口:封装所有模块的全局访问入口,开箱即用,无需手动创建模块。

整体架构运行流程:通过各模块接口实现,框架管理所有模块接口的注册,缓存和卸载,所有模块生命周期统一、实例唯一、自动管理。外部只需要通过一个统一的静态工具类调用所有的模块接口

二、核心源码逐层解析

下面逐层拆解整套架构的源码实现,附带设计思路和关键细节讲解,全部源码可直接落地使用。

1. 生命周期规范接口 IModule

首先定义统一的模块生命周期接口,规范所有业务模块的行为,保证架构统一性,避免自定义模块逻辑混乱。

/// <summary>
/// 所有业务模块统一生命周期接口
/// </summary>
public interface IModule
{
    /// <summary>
    /// 模块初始化
    /// </summary>
    void OnInit();

    /// <summary>
    /// 模块销毁
    /// </summary>
    void OnDestroy();
}

设计亮点:通过接口强制约束,所有接入架构的模块必须实现初始化和销毁逻辑,从根源避免资源遗忘释放的问题。

2. 模块通用基类 ModuleBase

实现接口默认空逻辑,作为所有业务管理器的父类,子类可按需重写生命周期方法,简化开发。

using Managers.Interface;

/// <summary>
/// 所有业务模块基类
/// </summary>
public class ModuleBase : IModule
{
    /// <summary>
    /// 模块初始化,子类重写实现自定义初始化逻辑
    /// </summary>
    public virtual void OnInit()
    {

    }

    /// <summary>
    /// 模块销毁,子类重写实现资源释放逻辑
    /// </summary>
    public virtual void OnDestroy()
    {

    }
}

3. 核心管控中心 ModuleManager

这是整套架构的核心,采用线程安全懒加载单例设计,通过字典统一缓存所有模块,提供注册、查找、卸载、清空全套能力。

using System.Collections.Generic;
using Managers.Interface;

/// <summary>
/// 全局模块统一管理器
/// 负责所有业务模块的注册、实例化、卸载、资源释放
/// </summary>
public class ModuleManager
{
    // 模块缓存字典:以接口名为Key,缓存对应模块实例
    private readonly Dictionary<string, ModuleBase> _moduleMap = new();

    // 单例锁对象,保证多线程安全
    private static readonly object LockObj = new object();
    private static ModuleManager _instance;

    private static ModuleManager Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (LockObj)
                {
                    _instance ??= new ModuleManager();
                }
            }
            return _instance;
        }
    }

    /// <summary>
    /// 注册模块,不存在则创建,存在则直接返回
    /// </summary>
    /// <typeparam name="T">模块接口类型</typeparam>
    /// <typeparam name="K">模块实现类型</typeparam>
    /// <returns>模块实例</returns>
    private ModuleBase RegisterModule<T,K>() where T:IModule where K:ModuleBase,T,new()
    {
        var moduleName = typeof(T).Name;
        // 避免重复注册模块
        if (_moduleMap.TryGetValue(moduleName, out var existing))
            return existing;

        // 实例化模块并执行初始化
        var module = new K();
        module.OnInit();
        _moduleMap[moduleName] = module;
        return module;
    }

    /// <summary>
    /// 对外统一获取模块接口,无模块则自动创建
    /// </summary>
    public static T GetOrCreateModule<T,K>() where T:IModule where K:ModuleBase,T,new()
    {
        var moduleName = typeof(T).Name;
        var module = Instance.FindModule(moduleName);
        return module == null ? (T)Instance.RegisterModule<T, K>() : (T)module;
    }

    /// <summary>
    /// 卸载指定模块
    /// </summary>
    public static void UnRegister<T>() where T : IModule
    {
        Instance.UnRegisterModule<T>();
    }

    /// <summary>
    /// 清空所有模块,全局释放资源
    /// </summary>
    public static void ClearAll()
    {
        Instance.ClearAllModules();
    }

    /// <summary>
    /// 查找已注册模块
    /// </summary>
    private IModule FindModule(string moduleName)
    {
        _moduleMap.TryGetValue(moduleName, out var module);
        return module;
    }

    /// <summary>
    /// 内部卸载指定模块,执行销毁逻辑并移除缓存
    /// </summary>
    public void UnRegisterModule<T>() where T : IModule
    {
        string key = typeof(T).Name;
        if (_moduleMap.TryGetValue(key, out var module))
        {
            module.OnDestroy();
            _moduleMap.Remove(key);
        }
    }

    /// <summary>
    /// 清空所有模块,批量释放资源
    /// </summary>
    public void ClearAllModules()
    {
        foreach (var module in _moduleMap.Values)
        {
            module.OnDestroy();
        }
        _moduleMap.Clear();
    }
}

核心关键设计解析:

  • 线程安全单例:双重校验锁实现懒加载,兼顾性能与多线程安全,适配Unity多线程加载场景;
  • 泛型约束解耦:通过接口T、实现类K双泛型约束,实现接口调用、实现隔离,符合面向接口编程思想;
  • 自动去重注册:注册模块前优先判断缓存,保证全局模块唯一,彻底杜绝多实例问题;
  • 生命周期闭环:模块创建自动执行OnInit,卸载/清空自动执行OnDestroy,资源管控闭环。

4. 全局统一入口 CoreUtils

封装静态工具类,为所有业务模块提供全局快捷访问入口,项目中任意位置直接调用,简洁优雅。

using Managers.Audio;
using Managers.FSM;
using Managers.Interface;
using Managers.Mono;
using Managers.Resources;
using Managers.UIM;

/// <summary>
/// 全局管理器快捷访问工具
/// 所有业务模块统一从此处调用
/// </summary>
public static class CoreUtils
{
    public static IUIManager UIManager => GetManager<IUIManager, UIManager>();
    public static IMonoManager MonoManager => GetManager<IMonoManager, MonoManager>();
    public static IResourcesManager ResourcesManager => GetManager<IResourcesManager, ResourcesManager>();
    public static IFsmManager FsmManager => GetManager<IFsmManager, FsmManager>();
    public static IAudioManager AudioManager => GetManager<IAudioManager, AudioManager>();

    /// <summary>
    /// 泛型获取模块实例,自动创建
    /// </summary>
    private static T GetManager<T, K>() where T : class, IModule where K : ModuleBase, T, new()
    {
        return ModuleManager.GetOrCreateModule<T, K>();
    }
}

三、架构使用教程

整套架构搭建完成后,新增任意业务模块只需三步,无需写任何单例、缓存逻辑,极简高效。

步骤1:定义模块接口

新建模块接口,继承 IModule,定义模块对外暴露的功能方法

public interface IAudioManager : IModule
{
    void PlayBgMusic(string audioName);
    void PlaySound(string audioName);
}

步骤2:实现模块逻辑

新建管理器类,继承 ModuleBase + 实现自定义接口,重写生命周期方法

using Managers.Interface;

public class AudioManager : ModuleBase, IAudioManager
{
    // 初始化音频池、配置等
    public override void OnInit()
    {
        base.OnInit();
        Debug.Log("音频管理器初始化完成");
    }

    // 释放音频资源、清空缓存
    public override void OnDestroy()
    {
        base.OnDestroy();
        Debug.Log("音频管理器资源释放完成");
    }

    public void PlayBgMusic(string audioName)
    {
        // 背景音乐播放逻辑
    }

    public void PlaySound(string audioName)
    {
        // 音效播放逻辑
    }
}

步骤3:注册全局入口

CoreUtils 中添加一行属性,即可全局访问

public static IAudioManager AudioManager => GetManager<IAudioManager, AudioManager>();

步骤4:项目中全局调用

任意脚本中直接调用,模块首次访问自动初始化,无需手动实例化

// 播放背景音乐
CoreUtils.AudioManager.PlayBgMusic("MainBg");

// 场景切换卸载音频模块
ModuleManager.UnRegister<IAudioManager>();

// 游戏退出清空所有模块资源
ModuleManager.ClearAll();

四、架构核心优势对比

1. 传统单例架构痛点

  • 单例管理器泛滥,无法统一进行管理,代码极其混乱
  • 每个Manager重复编写单例代码,冗余度极高;
  • 单例分散,无统一销毁入口,极易内存泄漏;
  • 多线程场景下单例可能出现多实例、空指针报错;
  • 模块生命周期不统一,维护成本极高。

2. 本模块化架构优势

  • 零冗余代码:无需重复写单例,新增模块仅需实现业务逻辑;
  • 生命周期统一管控:全局统一初始化、卸载、资源释放,彻底解决内存泄漏;
  • 线程安全:核心管理器双重锁单例,适配多线程资源加载;
  • 接口解耦:面向接口编程,业务层只依赖接口,降低耦合,便于迭代重构;
  • 懒加载优化:模块按需创建,不占用多余内存,提升项目启动速度;
  • 极简调用:静态工具类统一入口,全局随处调用,开发效率拉满。
  • 扩展性强:新增模块只需要按照流程实现,然后接入框架中

五、总结

这套模块化统一管理器架构是一套轻量化、高复用、低耦合的Unity通用基础架构,彻底解决了传统单例泛滥的行业痛点。架构设计简洁无冗余,不依赖第三方插件,适配2D/3D、手游/端游各类Unity项目。

核心本质就是:用中心化管理替代分散单例,用标准化和统一化生命周期规范模块行为,用泛型接口实现解耦复用,极大提升项目规范性、可维护性和扩展性。