什麼是 Repository 模式
Repository 這個詞直譯過來倉庫、倉儲的意思。這個意思其實也能反應出 Repository 模式作用。App 開發中少不了對資料的操作,資料的來源可能有很多種:網絡、資料庫、檔案以及記憶體中的緩存。而 Repository 就相當于一個倉庫管理者,管理這些資料的存儲。當業務層想要擷取或者存儲資料時,隻需要通過 Repository 的管理者進行操作。這樣的好處就是:屏蔽資料來源的操作接口。對于業務層來說不必關心資料存在哪裡,以及如何存儲的。而且也符合我們元件化/子產品化架構設計的思想。即當我們更換資料儲存設備時,例如從 Android 系統 Sqlite 資料轉換為第三方的資料庫時,不會影響到業務邏輯。
設計模式
首先預覽一下 Repository 模式的設計類圖(歡迎拍磚)
IDataSource
是定義了資料來源接口,是根據具體的業務需要定義。一般來說,有增、删、改、查這幾個方法。
LocalRepository
封裝的是本地存儲方式,實作
IDataSource
接口。
RemoteRepository
封裝的是網絡存儲方式,實作
IDataSource
其中
LocalRepository
與
RemoteRepository
就是代表着各種存儲方式的具體實作。而
RepositoryFactory
就是傳說中的“倉庫管理者”,管理着各種存儲方式,它也是業務層與資料層互動的橋梁。
Show me the code
假設目前有個業務是擷取遠端資料的需求,如果本地有緩存資料則從本地擷取,否則從網絡中擷取。這樣的業務邏輯很常見,我們用 Repository
模式進行封裝。
首先預覽代碼總體結構
IDataSource
public interface IDataSource<T> {
void add(T t);
void delete(T t);
void update(T t);
List<T> queryAll();
T queryById(int id);
}
LocalRepository
public class LocalRepository implements IDataSource<Data> {
public LocalRepository() {
}
@Override
public void add(Data data) {
DBHelper.get().add(data);
}
@Override
public void delete(Data data) {
DBHelper.get().delete(data);
}
@Override
public void update(Data data) {
DBHelper.get().update(data);
}
@Override
public List<Data> queryAll() {
return DBHelper.get().queryAll();
}
@Override
public Data queryById(int id) {
return DBHelper.get().queryById(id);
}
}
RemoteRepository
public class RemoteRepository implements IDataSource<Data> {
@Override
public void add(Data data) {
DataApi.get().add(data);
}
@Override
public void delete(Data data) {
DataApi.get().delete(data);
}
@Override
public void update(Data data) {
DataApi.get().update(data);
}
@Override
public List<Data> queryAll() {
return DataApi.get().queryAll();
}
@Override
public Data queryById(int id) {
return DataApi.get().queryById(id);
}
}
RepositoryFactory
public class RepositoryFactory implements IDataSource<Data> {
private IDataSource<Data> local;
private IDataSource<Data> remote;
private static RepositoryFactory INSTANCE;
/**
* 使用Map實作一個記憶體緩存
*/
HashMap<String, Data> mCache = new HashMap<>();
private RepositoryFactory(@NonNull IDataSource<Data> local, @NonNull IDataSource<Data> remote) {
this.local = local;
this.remote = remote;
}
public static RepositoryFactory get(@NonNull IDataSource<Data> local, @NonNull IDataSource<Data> remote) {
if (INSTANCE == null) {
INSTANCE = new RepositoryFactory(local, remote);
}
return INSTANCE;
}
public static RepositoryFactory get() {
if (INSTANCE == null) {
INSTANCE = new RepositoryFactory(new LocalRepository(), new RemoteRepository());
}
return INSTANCE;
}
public void destory() {
INSTANCE = null;
}
@Override
public void add(Data data) {
local.add(data);
remote.add(data);
mCache.put(String.valueOf(data.id), data);
}
@Override
public void delete(Data data) {
local.delete(data);
remote.delete(data);
mCache.remove(String.valueOf(data.id));
}
@Override
public void update(Data data) {
local.update(data);
remote.update(data);
mCache.put(String.valueOf(data.id), data);
}
/**
* @return
*/
@Override
public List<Data> queryAll() {
List<Data> list = local.queryAll();
if (list.isEmpty()) {
list = remote.queryAll();
}
return list;
}
/**
* 這裡使用三級緩存擷取一個Data對象
*
* @param id
* @return
*/
@Override
public Data queryById(int id) {
Data data = mCache.get(String.valueOf(id));
if (data == null) {
data = local.queryById(id);
}
if (data == null) {
data = remote.queryById(id);
}
if (data != null) {
mCache.put(String.valueOf(id), data);
}
return data;
}
}
使用示例
Flowable.fromCallable(new Callable<List<Data>>() {
@Override
public List<Data> call() throws Exception {
List<Data> dataList = RepositoryFactory.get().queryAll();
return dataList;
}
}).observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Consumer<List<Data>>() {
@Override
public void accept(@NonNull List<Data> datas) throws Exception {
textView.setText("data size:" + datas.size());
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
textView.setText(throwable.getMessage());
}
});
這裡是直接使用了 RxJava2 進行調用,因為 Repository 是對資料的請求和通路,這個是耗時操作,故需要放在背景線程中進行。在實際的項目中一般都會使用 MVP 來封裝這一層。
本文Demo :
wecodexyz/Componentization參考文獻:
googlesamples/android-architecture