一谈到 『IoC』,有经验的程序员立刻会联想到控制反转,将建立对象的责任反转给工厂。IoC是依赖注入 『DI』 的核心,大名鼎鼎的Spring框架就是一个很是卓越的的控制反转、依赖注入框架。遗憾的是,咱们显然不能在Unity 3D中去使用Spring框架,但思想是相通的——IoC也好,控制反转也罢,本质上是一个工厂,或者又被称为容器,咱们能够本身维护一个工厂来实现对对象的管理,这也是本文的核心内容。git
工厂,顾名思义,就是生产对象的地方。若是以前没有接触过设计模式,你可能会疑惑,我直接使用 『new』 关键字难道不能建立对象吗?为何还要大费周章的让工厂来建立?固然这是没错的,直接使用 『new』 关键字很简洁,也很易懂,但你考虑过对象的释放吗?你可能会说不用考虑啊,GC会帮咱们回收啊。程序员
其实问题就出在这里,由于你没有考虑对象管理的动机,因此就不会有工厂这个概念。试想一下,使用ADO.NET或者JDBC去访问数据库,咱们是否是要先创建一个Connection,当工做结束后,Close了这个链接。当再一次须要链接数据库时,再创建一次Connection,这背后其实有隐患。由于和数据库创建链接是很是耗时的,只是咱们感觉不到。咱们能不能在关闭链接时,不销毁对象,而是将其放到一个对象池,当下一次请求来时,直接从对象池中获取。这就是工厂的动机,对对象的建立和释放进行管理,这样能够有效的提升效率。github
注:释放指的是对象实现了IDisposable接口的非托管资源,在uMVVM框架,工厂维护的都是托管资源,销毁由GC决定 数据库
在uMVVM框架中,我将工厂分为三类:单例(Singleton),临时(Transient),池(Pool)。设计模式
咱们能够为这三种工厂声明公共的接口:IObjectFactory,这是很是有必要的,方便在运行时根据需求动态的切换不一样工厂:多线程
public interface IObjectFactory
{
object AcquireObject(string className);
object AcquireObject(Type type);
object AcquireObject<TInstance>() where TInstance : class, new();
void ReleaseObject(object obj);
}复制代码
这个接口功能很简单,经过统一的入口对对象进行建立与销毁的管理。并发
有了统一的工厂的接口以后,接下来就是去实现对应的工厂了,第一个要实现的就是 Singleton Factory:框架
public class SingletonObjectFactory:IObjectFactory
{
/// <summary>
/// 共享的字典,不会由于不一样的SingletonObjectFactory对象返回不惟一的实例对象
/// </summary>
private static Dictionary<Type,object> _cachedObjects = null;
private static readonly object _lock=new object();
private Dictionary<Type, object> CachedObjects
{
get
{
lock (_lock)
{
if (_cachedObjects==null)
{
_cachedObjects=new Dictionary<Type, object>();
}
return _cachedObjects;
}
}
}
//...省略部分代码...
public object AcquireObject<TInstance>() where TInstance:class,new() {
var type = typeof(TInstance);
if (CachedObjects.ContainsKey(type))
{
return CachedObjects[type];
}
lock (_lock)
{
var instance=new TInstance();
CachedObjects.Add(type, instance);
return CachedObjects[type];
}
}
}复制代码
上述代码中,咱们须要定义一个全局的字典,用来存储全部的单例,值得注意的是,CachedObjects 字典是一个 static 类型,这代表这是一个共享的字典,不会由于不一样的SingletonObjectFactory对象返回不惟一的实例对象。函数
还有一点,单例模式最好考虑一下多线程并发问题,虽然这是一个 『伪』 需求,毕竟Unity 3D是个单线程应用程序,但 uMVVM 框架仍是考虑了多线程并发的问题,使用 lock 关键字,它必须是一个 static 类型,保证 lock 了同一个对象。ui
Transient Factory 是最容易实现的工厂,不用考虑多线程并发问题,也不用考虑Pool,对每一次请求返回一个不一样的对象:
public class TransientObjectFactory : IObjectFactory
{
//...省略部分代码...
public object AcquireObject<TInstance>() where TInstance : class, new() {
var instance = new TInstance();
return instance;
}
}复制代码
Pool Factory 相对来讲是比较复杂的工厂,它对 Transient Factory 进行了升级——建立实例前先去Pool中看看是否有未被使用的对象,有的话,那么直接取出返回,若是没有则向Pool中添加一个。
Pool的实现有两种形式,一种是内置了诸多对象,还有一种是初始时是一个空的池,而后再往里面添加对象。第一种效率更高,直接从池里面拿,而第二种更省内存空间,相似于懒加载,uMVVM 的对象池技术使用第二种模式。
public class PoolObjectFactory : IObjectFactory
{
/// <summary>
/// 封装的PoolData
/// </summary>
private class PoolData
{
public bool InUse { get; set; }
public object Obj { get; set; }
}
private readonly List<PoolData> _pool;
private readonly int _max;
/// <summary>
/// 若是超过了容器大小,是否限制
/// </summary>
private readonly bool _limit;
public PoolObjectFactory(int max, bool limit) {
_max = max;
_limit = limit;
_pool = new List<PoolData>();
}
private PoolData GetPoolData(object obj) {
lock (_pool)
{
for (var i = 0; i < _pool.Count; i++)
{
var p = _pool[i];
if (p.Obj == obj)
{
return p;
}
}
}
return null;
}
/// <summary>
/// 获取对象池中的真正对象
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
private object GetObject(Type type) {
lock (_pool)
{
if (_pool.Count > 0)
{
if (_pool[0].Obj.GetType() != type)
{
throw new Exception(string.Format("the Pool Factory only for Type :{0}", _pool[0].Obj.GetType().Name));
}
}
for (var i = 0; i < _pool.Count; i++)
{
var p = _pool[i];
if (!p.InUse)
{
p.InUse = true;
return p.Obj;
}
}
if (_pool.Count >= _max && _limit)
{
throw new Exception("max limit is arrived.");
}
object obj = Activator.CreateInstance(type, false);
var p1 = new PoolData
{
InUse = true,
Obj = obj
};
_pool.Add(p1);
return obj;
}
}
private void PutObject(object obj) {
var p = GetPoolData(obj);
if (p != null)
{
p.InUse = false;
}
}
public object AcquireObject(Type type) {
return GetObject(type);
}
public void ReleaseObject(object obj) {
if (_pool.Count > _max)
{
if (obj is IDisposable)
{
((IDisposable)obj).Dispose();
}
var p = GetPoolData(obj);
lock (_pool)
{
_pool.Remove(p);
}
return;
}
PutObject(obj);
}
}复制代码
上述的代码经过构造函数的 max 决定Pool的大小,limit 参数表示超过Pool容量时,是否能够再继续往Pool中添加数据。方法 GetObject 是最核心的方法,逻辑很是简单,获取对象以前先判断Pool中是否有未被使用的对象,若是有,则返回,若是没有,则根据 limit 参数再决定是否能够往Pool中添加数据。
工厂模式是最多见的设计模式,根据工厂的类型能够获取不一样形式的数据对象,好比单例数据、临时数据、亦或是对象池数据。这一章的工厂模式很重要,也是对下一篇对象的注入『Inject』作准备,故称之为理念先行。
源代码托管在Github上,点击此了解
欢迎关注个人公众号: