Unity應用架構設計(7)——IoC工廠理念先行

一談到 『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)。設計模式

  • 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

有了統一的工廠的接口以後,接下來就是去實現對應的工廠了,第一個要實現的就是 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

Transient Factory 是最容易實現的工廠,不用考慮多線程併發問題,也不用考慮Pool,對每一次請求返回一個不一樣的對象:

public class TransientObjectFactory : IObjectFactory
{
     //...省略部分代碼...

    public object AcquireObject<TInstance>() where TInstance : class, new() {
        var instance = new TInstance();
        return instance;
    }

}複製代碼

Pool Factory

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上,點擊此瞭解

歡迎關注個人公衆號:

相關文章
相關標籤/搜索