Unity 中的Repository模式

在Unity 中,数据的存储其实有很多地方,比如最常见的内存可以高速缓存一些临时数据,PlayerPrefs可以记录一些存档信息,TextAsset可以存一些配置信息,日志文件可以用IO操作写入,关系型数据结构可以使用Sqlite存储。Repository 是个很抽象的概念,操作的数据也不一定要在本地,很有可能是存在远程服务器,所以也支持以Web Service的形式对数据进行访问和持久化。

根据上述的描述,Repository 模式的架构图如下所示:

Unity应用架构设计(9)——构建统一的 Repository_java

可以看到,通过统一的接口,可以实现对不同存储介质的访问,甚至是访问远程数据。

定义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实现。

1.)定义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);  } }

2.)定义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());    }
 } }

3.)定义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());     }  } }

4.)定义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");      }    }  } }

5.)定义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());    }  } }

6.)定义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 对象来对不同的介质进行访问和持久化,这也是今后对缓存的实现做准备。

https://mp.weixin.qq.com/s/lRlQLN9ZcUa4bffsVCDg0w