Unity應用架構設計(9)——構建統一的 Repository


談到 『Repository』 倉儲模式,第一映像就是封裝了對數據的訪問和持久化。Repository 模式的理念核心是定義了一個規范,即接口『Interface』,在這個規范里面定義了訪問以及持久化數據的行為。開發者只要對接口進行特定的實現就可以滿足對不同存儲介質的訪問,比如存儲在Database,File System,Cache等等。軟件開發領域有非常多類似的想法,比如JDBC就是定義了一套規范,而具體的廠商MySql,Oracle根據此開發對應的驅動。

Unity 中的Repository模式

在Unity 3D中,數據的存儲其實有很多地方,比如最常見的內存可以高速緩存一些臨時數據,PlayerPrefs可以記錄一些存檔信息,TextAsset可以存一些配置信息,日志文件可以用IO操作寫入,關系型數據結構可以使用Sqlite存儲。Repository 是個很抽象的概念,操作的數據也不一定要在本地,很有可能是存在遠程服務器,所以也支持以Web Service的形式對數據進行訪問和持久化。

根據上述的描述,Repository 模式的架構圖如下所示:

可以看到,通過統一的接口,可以實現對不同存儲介質的訪問,甚至是訪問遠程數據。

定義Repository規范

Repository的規范就是接口,這個接口功能很簡單,封裝了數據增,刪,查,改的行為:

public interface IRepository<T> where T:class,new()
{
    void Insert(T instance);
    void Delete(T instance);
    void Update(T instance);
    IEnumerable<T> Select(Func<T,bool> func );
}

這只是一個最基本的定義,也是最基礎的操作,完全可以再做擴展。

值得注意的是,對於一些只讀數據,比如TextAssets,Insert,Delete,Update 往往不用實現。這就違反了『里式替換原則』,解決方案也很簡單,使用接口隔離,對於只讀的數據只實現 ISelectable 接口。但這往往會破環了我們的Repository結構,你可能會擴展很多不同的行為接口,從代碼角度很優化,但可讀性變差。所以,在uMVVM框架中,我為了保證Repository的完整性和可讀性,選擇違背『里式替換原則』。

開發者根據不同的存儲介質,決定不同的操作方法,這是顯而易見的,下面就是一些常見Repository實現。

定義UnityResourcesRepository:用來訪問Unity的資源TextAssets

public class UnityResourcesRepository<T> : IRepository<T> where T : class, new()
{
    //...省略部分代碼...
    public IEnumerable<T> Select(Func<T, bool> func)
    {
        List<T> items = new List<T>();
        try
        {
            TextAsset[] textAssets = Resources.LoadAll<TextAsset>(DataDirectory);
            for (int i = 0; i < textAssets.Length; i++)
            {
                TextAsset textAsset = textAssets[i];
                T item = Serializer.Deserialize<T>(textAsset.text);
                items.Add(item);
            }
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
        return items.Where(func);
    }
}

定義PlayerPrefsRepository:用來訪問和持久化一些存檔相關信息

public class PlayerPrefsRepository<T> : IRepository<T> where T : class, new()
{
    //...省略部分代碼...
    public void Insert(T instance)
    {
        try
        {
            string serializedObject = Serializer.Serialize<T>(instance, true);
            PlayerPrefs.SetString(KeysIndexName, serializedObject);
          
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
       
    }

}

定義FileSystemRepository:用來訪問和持久化一些日志相關信息

public class FileSystemRepository<T> : IRepository<T> where T:class,new()
{	
    //...省略部分代碼...
    public void Insert(T instance)
    {
        try
        {
            string filename = GetFilename(Guid.NewGuid());
            if (File.Exists(filename))
            {
                throw new Exception("Attempting to insert an object which already exists. Filename=" + filename);
            }

            string serializedObject = Serializer.Serialize<T>(instance, true);
            using (StreamWriter stream = new StreamWriter(filename))
            {
                stream.Write(serializedObject);
            }
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }

    }

}

定義MemoryRepository:用來高速緩存一些臨時數據

public class MemoryRepository<T> : IRepository<T> where T : class, new()
{
    
    //...省略部分代碼...

    private Dictionary<object, T> repository = new Dictionary<object, T>();

    public MemoryRepository()
    {
        FindKeyPropertyInDataType();
    }

    public void Insert(T instance)
    {
        try
        {
            var id = KeyPropertyInfo.GetValue(instance, null);
            repository[id] = instance;
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }

    private void FindKeyPropertyInDataType()
    {
        foreach (PropertyInfo propertyInfo in typeof(T).GetProperties())
        {
            object[] attributes = propertyInfo.GetCustomAttributes(typeof(RepositoryKey), false);
            if (attributes != null && attributes.Length == 1)
            {
                KeyPropertyInfo = propertyInfo;
            }
            else
            {
                throw new Exception("more than one repository key exist");
            }
        }
       
    }
}

定義DbRepository:用來操作關系型數據庫Sqlite

public class DbRepository<T> : IRepository<T> where T : class, new()
{
    private readonly SQLiteConnection _connection;

    //...省略部分代碼...

    public void Insert(T instance)
    {
        try
        {
            _connection.Insert(instance);
        }
        catch (Exception e)
        {
           throw new Exception(e.ToString());
        }
    }

}

定義RestRepository:以WebService的形式訪問和持久化遠程數據

public class RestRepository<T, R>:IRepository<T> where T : class, new() where R : class, new()
{
    //...省略部分代碼...

    public void Insert(T instance)
    {
        //通過WWW像遠程發送消息
    }
}

小結

Repository 模式是很常見的數據層技術,對於.NET 程序員來說就是DAL,而對於Java程序員而言就是DAO。我們擴展了不同的Repository 對象來對不同的介質進行訪問和持久化,這也是今后對緩存的實現做准備。
源代碼托管在Github上,點擊此了解


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM