代理模式(Proxy Pattern)是一种结构型设计模式,它为其他对象提供了一种代理以控制对该对象的访问。代理模式的核心是通过一个代理对象来控制对目标对象的访问,可以在访问目标对象之前或之后添加额外的行为。
在Unity中,代理模式非常适合用来控制资源的访问(如懒加载资源、网络请求封装)或添加一些附加功能(如日志记录、访问权限检查等)。
1. 代理模式的基本概念
核心结构
代理模式包含以下主要角色:
- 抽象主题(Subject):定义目标对象和代理对象的公共接口,客户端通过这个接口与目标对象或代理对象交互。
- 真实主题(RealSubject):实现抽象主题接口,定义目标对象的实际行为。
- 代理(Proxy):实现抽象主题接口,持有对真实主题的引用,控制对真实主题的访问。
优点
- 控制对象访问:通过代理可以控制对目标对象的访问权限。
- 延迟加载:可以延迟加载资源,提高性能。
- 增强功能:在访问目标对象之前或之后添加额外的行为。
缺点
- 增加复杂性:引入代理对象会增加系统的复杂性。
- 性能开销:代理对象可能会增加调用的开销,尤其是远程代理。
2. Unity 实现代理模式
以下通过一个 虚拟资源加载系统 的示例,展示如何在Unity中实现代理模式。该系统包括:
- 一个真实资源加载器(
RealResourceLoader
),负责加载资源。 - 一个代理资源加载器(
ProxyResourceLoader
),负责控制资源的加载逻辑(如延迟加载、日志记录等)。
3. 实现步骤
(1) 定义抽象主题接口
创建一个接口,定义资源加载的公共行为。
// IResourceLoader.cs
public interface IResourceLoader
{
void LoadResource(string resourceName); // 加载资源的方法
}
(2) 实现真实主题
创建一个真实资源加载器,负责实际的资源加载逻辑。
// RealResourceLoader.cs
using UnityEngine;
public class RealResourceLoader : IResourceLoader
{
public void LoadResource(string resourceName)
{
Debug.Log($"Loading resource: {resourceName}");
// 模拟资源加载逻辑
}
}
(3) 实现代理
创建一个代理资源加载器,控制对真实资源加载器的访问。在这里可以添加日志记录或延迟加载逻辑。
// ProxyResourceLoader.cs
using UnityEngine;
public class ProxyResourceLoader : IResourceLoader
{
private RealResourceLoader _realLoader;
public void LoadResource(string resourceName)
{
if (_realLoader == null)
{
_realLoader = new RealResourceLoader(); // 延迟初始化真实加载器
}
Debug.Log("Proxy: Checking access before loading resource...");
_realLoader.LoadResource(resourceName); // 调用真实加载器的加载方法
Debug.Log("Proxy: Resource loading completed.");
}
}
(4) 测试代理模式
创建一个测试脚本,模拟资源加载的过程。
// GameManager.cs
using UnityEngine;
public class GameManager : MonoBehaviour
{
void Start()
{
// 使用代理加载资源
IResourceLoader resourceLoader = new ProxyResourceLoader();
Debug.Log("Requesting resource loading...");
resourceLoader.LoadResource("PlayerModel"); // 加载玩家模型资源
Debug.Log("Requesting another resource loading...");
resourceLoader.LoadResource("EnemyModel"); // 加载敌人模型资源
}
}
4. 运行结果
运行游戏后,控制台输出如下:
Requesting resource loading...
Proxy: Checking access before loading resource...
Loading resource: PlayerModel
Proxy: Resource loading completed.
Requesting another resource loading...
Proxy: Checking access before loading resource...
Loading resource: EnemyModel
Proxy: Resource loading completed.
5. 扩展性
(1) 添加访问控制
可以在代理中添加权限检查逻辑。例如,只有管理员角色可以加载某些资源:
// ProxyResourceLoader.cs
public class ProxyResourceLoader : IResourceLoader
{
private RealResourceLoader _realLoader;
public void LoadResource(string resourceName)
{
if (!HasAccess(resourceName))
{
Debug.LogWarning("Proxy: Access denied for resource: " + resourceName);
return;
}
if (_realLoader == null)
{
_realLoader = new RealResourceLoader();
}
Debug.Log("Proxy: Checking access before loading resource...");
_realLoader.LoadResource(resourceName);
Debug.Log("Proxy: Resource loading completed.");
}
private bool HasAccess(string resourceName)
{
// 模拟权限检查
return resourceName != "RestrictedResource";
}
}
测试:
resourceLoader.LoadResource("RestrictedResource");
// 输出:
// Proxy: Access denied for resource: RestrictedResource
(2) 添加缓存逻辑
可以在代理中添加缓存逻辑,以减少重复加载的性能开销:
// ProxyResourceLoader.cs
using System.Collections.Generic;
public class ProxyResourceLoader : IResourceLoader
{
private RealResourceLoader _realLoader;
private HashSet<string> _loadedResources = new HashSet<string>();
public void LoadResource(string resourceName)
{
if (_loadedResources.Contains(resourceName))
{
Debug.Log($"Proxy: Resource {resourceName} is already loaded. Using cached version.");
return;
}
if (_realLoader == null)
{
_realLoader = new RealResourceLoader();
}
Debug.Log("Proxy: Checking access before loading resource...");
_realLoader.LoadResource(resourceName);
_loadedResources.Add(resourceName);
Debug.Log("Proxy: Resource loading completed.");
}
}
测试:
resourceLoader.LoadResource("PlayerModel");
// 输出:
// Proxy: Checking access before loading resource...
// Loading resource: PlayerModel
// Proxy: Resource loading completed.
resourceLoader.LoadResource("PlayerModel");
// 输出:
// Proxy: Resource PlayerModel is already loaded. Using cached version.
(3) 应用到其他场景
- 网络请求代理:代理模式可以封装网络请求逻辑,添加日志或重试机制。
- 权限代理:限制某些功能的访问(如菜单选项、管理面板)。
- 虚拟代理:延迟初始化某些耗资源的对象(如大型纹理、模型)。
6. 优化与注意事项
(1) 代理的职责不要过多
代理的职责应该集中在控制访问,而不应替代真实主题的核心功能。
(2) 避免性能瓶颈
如果代理在访问过程中引入了大量的额外逻辑(例如频繁的权限检查或复杂的缓存机制),可能会导致性能下降。
(3) 真实主题的多态性
确保代理与真实主题实现相同的接口,以便客户端可以透明地使用代理或真实主题。
7. 总结
代理模式的优缺点
优点 | 缺点 |
---|---|
控制对目标对象的访问,提供额外功能。 | 引入了额外的代理对象,增加了系统复杂性。 |
支持延迟加载、权限检查、日志记录等功能扩展。 | 可能会引入性能开销(例如频繁的权限检查或缓存逻辑)。 |
真实主题可以集中于核心功能,代理负责辅助功能。 | 需要确保代理与真实主题的接口一致,维护成本可能较高。 |
适用场景
- 需要控制对目标对象的访问(如权限检查、日志记录)。
- 需要延迟加载资源或对象(如虚拟代理)。
- 需要在访问目标对象前后添加额外的行为。
通过代理模式,Unity项目的功能扩展可以变得更加灵活,同时也可以提高系统的安全性和可维护性。