4、Repository模式:单一数据源原则,Repository的职责与设计
好,我们直接进入正题。Repository模式,说白了就是给数据访问加个「中间人」。你想想看,ViewModel想要数据,它不用管数据是从网络来的,还是从本地数据库来的,它只需要跟Repository说一声「给我数据」,Repository就会搞定一切。
我在项目中遇到过很多次,团队里有人直接在ViewModel里写网络请求,结果后来要加缓存,改得那叫一个痛苦。嗯,这就是典型的「没有中间商赚差价」的后果。
4.1 单一数据源原则
单一数据源,英文叫Single Source of Truth,简称SSOT。这个原则的核心思想是:数据只有一个权威来源。
举个例子,你的App里显示用户信息。如果网络请求返回了用户数据,本地数据库也存了一份,那到底以哪个为准?
核心结论:Repository就是那个「唯一权威」。所有数据请求都经过它,所有数据更新也由它统一管理。
我曾经接手过一个项目,网络层和数据库层各自为政。结果用户改了昵称,列表页显示新昵称,详情页还是旧昵称。排查了半天,发现是网络请求和本地缓存没有同步。从那以后,我强制要求所有数据必须走Repository。
4.2 Repository的职责
Repository到底该干什么?我总结下来就三件事:
- 数据调度:决定从网络拿数据,还是从本地拿数据
- 数据转换:把网络返回的DTO(Data Transfer Object)转成Domain层需要的Model
- 数据同步:确保网络数据和本地数据保持一致
说白了,Repository就是数据层的「交通警察」。它不生产数据,它只是数据的搬运工。
4.3 典型设计
来看一个我常用的设计模板。先定义接口:
interface UserRepository {
suspend fun getUser(id: String): Result<User>
suspend fun updateUser(user: User): Result<Unit>
suspend fun deleteUser(id: String): Result<Unit>
}
然后是实现类:
class UserRepositoryImpl(
private val remoteDataSource: UserRemoteDataSource,
private val localDataSource: UserLocalDataSource
) : UserRepository {
override suspend fun getUser(id: String): Result<User> {
return try {
// 先尝试从网络获取
val remoteUser = remoteDataSource.getUser(id)
// 同步到本地
localDataSource.saveUser(remoteUser)
Result.success(remoteUser.toDomain())
} catch (e: Exception) {
// 网络失败,从本地拿缓存
val localUser = localDataSource.getUser(id)
if (localUser != null) {
Result.success(localUser.toDomain())
} else {
Result.failure(e)
}
}
}
}
我的习惯:Repository只返回Result类型,不抛异常。这样上层处理起来更干净,不用到处try-catch。
4.4 数据流设计
来看一张我画的流程图,帮你理解数据是怎么流转的:
你看,ViewModel只跟Repository打交道。Repository先尝试网络,失败了就回退到本地缓存。这就是典型的「离线优先」策略。
4.5 避坑指南
我曾经踩过的坑:
- 不要在Repository里做UI相关的逻辑,比如格式化时间、拼接字符串。那是ViewModel的事。
- 不要直接暴露DataSource给ViewModel。我见过有人图省事,在Repository里加了个getRemoteDataSource()方法,结果ViewModel直接调了网络接口,缓存策略形同虚设。
- 注意线程安全。如果多个ViewModel同时请求数据,Repository要做好并发控制。我一般用Kotlin的Mutex或者Channel来处理。
4.6 什么时候用Repository?
不是所有项目都需要Repository。我个人的判断标准很简单:
| 场景 | 是否推荐Repository | 原因 |
|---|---|---|
| 只有一个数据源(比如纯网络) | 可选 | 简单项目可以直接用ViewModel+网络层 |
| 多个数据源(网络+缓存+数据库) | 强烈推荐 | 统一管理数据来源,避免混乱 |
| 需要离线功能 | 必须 | Repository是离线策略的最佳载体 |
| 团队协作项目 | 推荐 | 职责清晰,新人上手快 |
我的建议:哪怕项目初期只有一个数据源,我也建议先搭好Repository的架子。因为后期加缓存、加数据源是大概率事件。到时候再重构,成本比现在搭架子高得多。
4.7 小结
Repository模式的核心就一句话:让数据访问变得简单、统一、可测试。
你想想看,ViewModel不需要知道数据从哪来,测试的时候只需要mock Repository接口,多清爽。这就是Clean Architecture里依赖倒置原则的体现——高层模块不依赖低层模块,大家都依赖抽象。
嗯,这一章的内容就到这里。记住,Repository不是银弹,但它确实是Android开发中管理数据的最佳实践之一。
公众号:蓝海资料掘金营,微信deep3321