SingletonSystem 单例管理系统
单例模式是项目中最为常见的设计模式之一,但是写法都不够优雅不方便查找和管理,下面介绍一种使用反射实现的单例管理架构,需要基于之前介绍过的AssemblyManager 程序集管理器
1.AssemblyManager
每次加载程序集的时候会有相应的事件回调机制,并且AssemblyManager 提供了遍历程序集中的类或者实现某个接口的类,使用此功能便能实现比较优雅的单例管理系统
2.定义两个接口ISingleton、IUpdateSingleton
public interface ISingleton : IDisposable
{
public bool IsDisposed { get; set; }
Task Initialize();
}
public interface IUpdateSingleton : ISingleton
{
public void Update();
}
3.单例基类Singleton
public abstract class Singleton<T> : ISingleton where T : ISingleton, new()
{
public bool IsDisposed { get; set; }
public static T Instance { get; private set; }
private void RegisterSingleton(ISingleton singleton)
{
Instance = (T)singleton;
IsDisposed = false;
AssemblyManager.OnLoadAssemblyEvent += Load;
AssemblyManager.OnUnLoadAssemblyEvent += UnLoad;
}
protected abstract void UnLoad(int assemblyName);
protected abstract void Load(int assemblyName);
public virtual Task Initialize()
{
return Task.CompletedTask;
}
public virtual void Dispose()
{
IsDisposed = true;
Instance = default;
AssemblyManager.OnLoadAssemblyEvent -= Load;
AssemblyManager.OnUnLoadAssemblyEvent -= UnLoad;
}
}
4.SingletonSystem单例管理系统
首先用队列存储所有程序集中单例实现Update的接口,便于每帧更新
其次使用多值队列存储某个程序集中所有的单例
private static readonly Queue<IUpdateSingleton> updateSingletons = new Queue<IUpdateSingleton>();
private static readonly OneToManyQueue<int, ISingleton> singletons = new OneToManyQueue<int, ISingleton>();
初始化SingletonSystem,需要外界手动调用
public static void Initialize()
{
AssemblyManager.OnLoadAssemblyEvent += Load;
AssemblyManager.OnUnLoadAssemblyEvent += UnLoad;
}
单例系统的释放,需要外界手动调用
public static void Dispose()
{
foreach (var item in singletons.Values)
{
UnLoad(item);
}
updateSingletons.Clear();
singletons.Clear();
AssemblyManager.OnLoadAssemblyEvent -= Load;
AssemblyManager.OnUnLoadAssemblyEvent -= UnLoad;
}
其余加载卸载
private static void UnLoad(int assemblyName)
{
if (!singletons.TryGetValue(assemblyName, out var queue))
return;
UnLoad(queue);
singletons.RemoveKey(assemblyName);
}
private static void Load(int assemblyName)
{
List<Task> tasks = new List<Task>();
UnLoad(assemblyName);
foreach (Type singletonType in AssemblyManager.ForEach(assemblyName,typeof(ISingleton)))
{
var instance = (ISingleton)Activator.CreateInstance(singletonType);
MethodInfo registerMethodInfo = singletonType.BaseType?.GetMethod("RegisterSingleton", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo initializeMethodInfo = singletonType.GetMethod("Initialize", BindingFlags.Instance | BindingFlags.Public);
MethodInfo onLoadMethodInfo = singletonType.GetMethod("Load", BindingFlags.Instance | BindingFlags.NonPublic);
if (initializeMethodInfo != null)
{
tasks.Add((Task)initializeMethodInfo.Invoke(instance, null));
}
registerMethodInfo?.Invoke(instance, new object[] { instance });
onLoadMethodInfo?.Invoke(instance, new object[] { assemblyName });
switch (instance)
{
case IUpdateSingleton updateSingleton:
updateSingletons.Enqueue(updateSingleton);
break;
default:
break;
}
singletons.Enqueue(assemblyName, instance);
}
Task.WaitAll(tasks.ToArray());
}
public static void Update()
{
int updateCount = updateSingletons.Count;
while (updateCount-- >0)
{
IUpdateSingleton updateSingleton = updateSingletons.Dequeue();
if (updateSingleton.IsDisposed)
continue;
updateSingletons.Enqueue(updateSingleton);
try
{
updateSingleton.Update();
}
catch (Exception ex)
{
Debug.LogError(ex.Message);
}
}
}
private static void UnLoad(Queue<ISingleton> queue)
{
if (queue == null)
return;
while (queue.Count > 0)
{
try
{
queue.Dequeue().Dispose();
}
catch (Exception ex)
{
Debug.LogError(ex.Message);
}
}
}
5.测试
测试单例
public class TestSingleton : Singleton<TestSingleton>,IUpdateSingleton
{
public override Task Initialize()
{
Debug.Log("TestSingleton 初始化");
return base.Initialize();
}
protected override void Load(int assemblyName)
{
}
protected override void UnLoad(int assemblyName)
{
Debug.Log("卸载!");
}
public void TestFunc()
{
Debug.Log("调用单例测试方法!!!");
}
public void Update()
{
Debug.Log("单例Update方法!!!");
}
}
public class Test : MonoBehaviour
{
void Start()
{
SingletonSystem.Initialize();
AssemblyManager.Initialize();
TestSingleton.Instance.TestFunc();
}
private void Update()
{
SingletonSystem.Update();
if (Input.GetKeyDown(KeyCode.P))
{
TestSingleton.Instance.TestFunc();
}
}
}