談到 『Repository』 倉儲模式,第一映像就是封裝了對數據的訪問和持久化。Repository 模式的理念核心是定義了一個規範,即介面『Interface』,在這個規範裡面定義了訪問以及持久化數據的行為。開發者只要對介面進行特定的實現就可以滿足對不同存儲介質的訪問,比如存儲在Database ...
談到 『Repository』 倉儲模式,第一映像就是封裝了對數據的訪問和持久化。Repository 模式的理念核心是定義了一個規範,即介面『Interface』,在這個規範裡面定義了訪問以及持久化數據的行為。開發者只要對介面進行特定的實現就可以滿足對不同存儲介質的訪問,比如存儲在Database,File System,Cache等等。軟體開發領域有非常多類似的想法,比如JDBC就是定義了一套規範,而具體的廠商MySql,Oracle根據此開發對應的驅動。
Unity 3D中的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上,點擊此瞭解