概述
A Repository mediates between the domain and data mapping layers, acting like an in-memory domain object collection.
Repository是一个独立的层,介于领域层与数据映射层(数据访问层)之间。它的存在让领域层感觉不到数据访问层的存在,它提供一个类似集合的接口提供给领域层进行领域对象的访问。Repository是仓库管理员,领域层需要什么东西只需告诉仓库管理员,由仓库管理员把东西拿给它,并不需要知道东西实际放在哪。(摘自网络)
一、为什么要使用Repository
1、直接访问数据的缺点
在许多应用程序中,业务逻辑从数据存储(如数据库,SharedPreferences或Web服务)访问数据。直接访问数据可能会导致以下结果:
- 重复的代码
- 编程错误的可能性更高
- 业务数据的类型很弱
- 难以集中数据相关的策略,如缓存
- 无法轻松地与外部依赖关系单独测试业务逻辑
2.如何解决直接访问数据带来的问题
Use a repository to separate the logic that retrieves the data and maps it to the entity model from the business logic that acts on the model
(使用存储库分离数据映射的逻辑,并将其从作用于模型的业务逻辑映射到实体模型)
数据和业务层之间的分离有三个好处:
- 它集中了数据逻辑或Web服务访问逻辑。
- 它为单元测试提供了替换点。
- 它提供了一种灵活的架构,可以随着应用程序的整体设计的发展而进行调整。
3.使用Repository模式的好处
- 隔离数据层以支持单元测试,提高测试效率。单元测试时,用Mock对象代替实际的数据库存取,可以成倍地提高测试用例运行速度。
- 将业务逻辑与数据或服务访问逻辑分离来提高代码的可维护性和可读性。
- 实现并集中数据源的缓存策略。
- 从许多位置访问数据源,并希望应用集中管理的一致访问规则和逻辑。
二、如何实现Repository模式
Repository模式是一个中间层,位于 数据库映射层 和 领域层(业务逻辑层)之间,数据访问逻辑和业务逻辑只能通过接口来进行数据操作.这样就隐含着一种意图倾向,就是业务逻辑需要什么我才提供什么,不该提供的功能就不要提供,一切都是以业务逻辑的需求为核心。CURD 在到具体的 Repository 上去做实现,业务层不需要知道它的具体实现,达到了分离关注点。
1.Repository层
以登录后把用户信息保存到SharedPreferences中为例子
Repository接口
public interface IUserRepository {
interface DataCallback {
void success(T t);
void failure(String errorMsg);
}
void login(String username, String password, DataCallback callback);
}
Repository实现
public class UserRepository implements IUserRepository {
private static UserRepository INSTANCE = null;
private final UserService userService;
private final IPreferencesDataSource preferencesDataSource;
private UserRepository(@NonNull UserService userService,
@NonNull IPreferencesDataSource preferencesDataSource) {
this.userService = userService;
this.preferencesDataSource = preferencesDataSource;
}
public static UserRepository getInstance(UserService userService,
IPreferencesDataSource preferencesDataSource) {
if (INSTANCE == null) {
INSTANCE = new UserRepository(userService, preferencesDataSource);
}
return INSTANCE;
}
@Override
public void login(String username, String password, DataCallback callback) {
userService.login(username, password).enqueue(new RequestCallBack>() {
@Override
public void success(BaseResponse userBaseResponse) {
if (userBaseResponse.getErrorCode() == SUCCESS_CODE) {
preferencesDataSource.saveUser(userBaseResponse.getData());
callback.success(userBaseResponse.getData());
} else {
callback.failure(userBaseResponse.getErrorMsg());
}
}
@Override
public void failure(String errorMsg) {
callback.failure(errorMsg);
}
});
}
}
- 在Repository中可以随意替换数据持久的方式,也可以增加数据缓存。
构建工厂
public class DataSourceFactory {
public static UserRepository getUserRepository() {
final UserService userService = ApiClient.getInstance().create(UserService.class);
final PreferencesDataSource preferencesDataSource = PreferencesDataSource.getInstance(App.getInstance());
return UserRepository.getInstance(userService, preferencesDataSource);
}
}
- DataSource层
SharedPreferences
public class PreferencesDataSource implements IPreferencesDataSource {
private static final String SP_NAME_USER = "user_info";
private static PreferencesDataSource INSTANCE = null;
private Context mContext;
public static PreferencesDataSource getInstance(@NonNull Context context) {
if (INSTANCE == null) {
INSTANCE = new PreferencesDataSource(context.getApplicationContext());
}
return INSTANCE;
}
private PreferencesDataSource(@NonNull Context context) {
this.mContext = context;
}
@Override
public void saveUser(User user) {
SharedPreferences preferences = mContext.getSharedPreferences(SP_NAME_USER, Context.MODE_PRIVATE);
preferences.edit()
.putString("user_name", user.getUsername())
.putString("user_password", user.getPassword())
.putString("user_icon", user.getIcon())
.putInt("user_type", user.getType())
.apply();
}
@Override
public User loadUser() {
User user = new User();
SharedPreferences preferences = mContext.getSharedPreferences(SP_NAME_USER, Context.MODE_PRIVATE);
user.setUsername(preferences.getString("user_name", ""));
user.setPassword(preferences.getString("user_password", ""));
user.setIcon(preferences.getString("user_icon", ""));
user.setType(preferences.getInt("user_type", -1));
return user;
}
}
Network
public interface UserService {
@POST("user/login")
@FormUrlEncoded
Call> login(@Field("username") String username, @Field("password") String password);
}
public class ApiClient {
.....
public T create(Class clazz) {
return mRetrofit.create(clazz);
}
}
接口访问直接使用Retrofit的Service
3.我项目中Repository的结构
一个简单的购物APP
建议分出多个Retrofit的Service对应多个Repository,不同的Repository注入不同的Service并实现具体的逻辑。
- UserService
- TradingService
- GoodsService
总结
使用Repository模式会增加很多代码但是所带来的好处,远高于实现这个模式所增加的代码。
参考文档:
- Microsoft Repository Pattern
- android-architecture TasksRepository