Unity开发中如何将具体实现与MonoBehaviour类进行抽象解耦
Unity MonoBehaviour与业务实现解耦的可行方案
最适配Unity原生生态的方案是自定义轻量服务定位器,不需要引入第三方DI框架,也不会出现生命周期不兼容问题:
- 第一步:实现全局服务定位器
using System; using System.Collections.Generic; public static class ServiceLocator { private static readonly Dictionary<Type, object> _services = new Dictionary<Type, object>(); // 统一注册服务,仅在游戏启动入口调用一次 public static void Register<T>(T service) where T : class { var type = typeof(T); if (!_services.ContainsKey(type)) { _services.Add(type, service); } } // 获取服务,所有Mono调用该方法拿接口实例 public static T Get<T>() where T : class { _services.TryGetValue(typeof(T), out var service); return service as T; } }
- 第二步:在游戏全局启动入口(比如挂载在初始场景永不销毁的GameRoot脚本)统一注册服务,仅需在此处处理构造函数逻辑
public class GameRoot : MonoBehaviour { private void Awake() { DontDestroyOnLoad(gameObject); // FileHandler构造函数变更时仅需修改此处一行代码 ServiceLocator.Register<IFileHandler>(new FileHandler(/* 构造参数 */)); // 其余所有服务都可在此处统一注册 } }
- 第三步:修改FileHandling类,完全解除和具体FileHandler的耦合
public class FileHandling : MonoBehaviour { private IFileHandler fileHandler; private void Awake() { // 直接拿接口实例,不需要关注实现类的构造逻辑 this.fileHandler = ServiceLocator.Get<IFileHandler>(); } }
如果你不想用服务定位器,也可以用Unity原生特性实现注入:
Unity 2020.1及以上版本支持[SerializeReference]特性,可以直接在Inspector面板序列化接口实例,你可以直接将服务实例的赋值放到Inspector层面,不需要硬编码构造逻辑:
public class FileHandling : MonoBehaviour { [SerializeReference] private IFileHandler fileHandler; // 不需要在Awake里手动实例化,直接在Inspector拖入对应实现类实例即可 }
如果你的服务是跨场景通用的单例服务,也可以将实现类继承自ScriptableObject,做成全局配置资源存放在Resources目录,启动时自动加载,同样只需要修改一次实例化逻辑。
注意:第三方DI框架不推荐在Unity中使用的核心原因是,Unity的GameObject生命周期(Awake/Start/销毁/场景卸载)和常规.NET程序的生命周期逻辑差异很大,很容易出现注入实例过期、资源泄漏、生命周期不匹配的问题,上述原生适配方案的稳定性和性能都更适合Unity项目。
内容的提问来源于stack exchange,提问作者Nils
相关产品推荐
相关产品推荐

